From da7e46ab5852cb9fa8f9aed84f46a74b0bb8f05a Mon Sep 17 00:00:00 2001 From: David Schultz Date: Thu, 31 Aug 2023 18:11:05 -0500 Subject: [PATCH] disk usage (#2) * more setup * update setup.cfg * update dependencies*.log files(s) * update dependencies*.log files(s) * update dependencies*.log files(s) * update dependencies*.log files(s) * try with new setup action * update dependencies*.log files(s) * update dependencies*.log files(s) * update dependencies*.log files(s) * add python server --------- Co-authored-by: github-actions --- Dockerfile | 2 +- cephfs_disk_usage/__main__.py | 40 ++ cephfs_disk_usage/server.py | 234 ++++++++++++ cephfs_disk_usage/static/apple-touch-icon.png | Bin 0 -> 8413 bytes cephfs_disk_usage/static/boilerplate.css | 360 ++++++++++++++++++ cephfs_disk_usage/static/boilerplate.js | 23 ++ cephfs_disk_usage/static/favicon.png | Bin 0 -> 3084 bytes cephfs_disk_usage/static/icecube-logo_60.png | Bin 0 -> 13888 bytes cephfs_disk_usage/static/style.css | 84 ++++ cephfs_disk_usage/templates/base.html | 30 ++ cephfs_disk_usage/templates/details.html | 15 + cephfs_disk_usage/templates/main.html | 21 + cephfs_disk_usage/templates/piechart.html | 1 + cephfs_disk_usage/templates/piechart.js | 21 + cephfs_disk_usage/templates/table.html | 31 ++ setup.cfg | 19 +- setupenv.sh | 6 + 17 files changed, 883 insertions(+), 4 deletions(-) create mode 100644 cephfs_disk_usage/__main__.py create mode 100644 cephfs_disk_usage/server.py create mode 100644 cephfs_disk_usage/static/apple-touch-icon.png create mode 100644 cephfs_disk_usage/static/boilerplate.css create mode 100644 cephfs_disk_usage/static/boilerplate.js create mode 100644 cephfs_disk_usage/static/favicon.png create mode 100644 cephfs_disk_usage/static/icecube-logo_60.png create mode 100644 cephfs_disk_usage/static/style.css create mode 100644 cephfs_disk_usage/templates/base.html create mode 100644 cephfs_disk_usage/templates/details.html create mode 100644 cephfs_disk_usage/templates/main.html create mode 100644 cephfs_disk_usage/templates/piechart.html create mode 100644 cephfs_disk_usage/templates/piechart.js create mode 100644 cephfs_disk_usage/templates/table.html create mode 100755 setupenv.sh diff --git a/Dockerfile b/Dockerfile index 370649e..48009fc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10 +FROM python:3.11 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y attr diff --git a/cephfs_disk_usage/__main__.py b/cephfs_disk_usage/__main__.py new file mode 100644 index 0000000..3e51925 --- /dev/null +++ b/cephfs_disk_usage/__main__.py @@ -0,0 +1,40 @@ +import asyncio +import logging + +from wipac_dev_tools import from_environment + +from .server import Server + +# handle logging +setlevel = { + 'CRITICAL': logging.CRITICAL, # execution cannot continue + 'FATAL': logging.CRITICAL, + 'ERROR': logging.ERROR, # something is wrong, but try to continue + 'WARNING': logging.WARNING, # non-ideal behavior, important event + 'WARN': logging.WARNING, + 'INFO': logging.INFO, # initial debug information + 'DEBUG': logging.DEBUG # the things no one wants to see +} + +default_config = { + 'LOG_LEVEL': 'INFO', +} +config = from_environment(default_config) +if config['LOG_LEVEL'].upper() not in setlevel: + raise Exception('LOG_LEVEL is not a proper log level') +logformat = '%(asctime)s %(levelname)s %(name)s %(module)s:%(lineno)s - %(message)s' + +logging.basicConfig(format=logformat, level=setlevel[config['LOG_LEVEL'].upper()]) + + +# start server +async def main(): + s = Server() + await s.start() + try: + await asyncio.Event().wait() + finally: + await s.stop() + + +asyncio.run(main()) diff --git a/cephfs_disk_usage/server.py b/cephfs_disk_usage/server.py new file mode 100644 index 0000000..a57e109 --- /dev/null +++ b/cephfs_disk_usage/server.py @@ -0,0 +1,234 @@ +""" +Server +""" + +import asyncio +from dataclasses import dataclass as dc +import json +import logging +import os +from pathlib import Path +import stat +from typing import Self + +from tornado.web import RequestHandler, HTTPError +from rest_tools.server import RestServer +from wipac_dev_tools import from_environment + +from . import __version__ as version + +logger = logging.getLogger('server') + + +class Error(RequestHandler): + def prepare(self): + raise HTTPError(404, 'invalid route') + + +class BaseHandler(RequestHandler): + def initialize(self, filesystems): + self.filesystems = filesystems + + def set_default_headers(self): + self._headers['Server'] = f'CephFS Disk Usage {version}' + + def get_template_namespace(self): + ret = super().get_template_namespace() + ret['os'] = os + ret['json_encode'] = json.dumps + return ret + + +class Health(BaseHandler): + async def get(self): + ret = {} + for k in self.filesystems: + ret[k] = await self.filesystems[k].status() + self.write(ret) + + +class Main(BaseHandler): + async def get(self): + paths = {} + for k in self.filesystems: + paths[k] = await self.filesystems[k].dir_entry('/') + self.render('main.html', paths=paths) + + +class Details(BaseHandler): + async def get(self, path): + for fs in self.filesystems: + if path.startswith(fs): + data = await self.filesystems[fs].dir_entry(path[len(fs):]) + break + else: + raise HTTPError(400, 'bad path') + + self.render('details.html', path=path, data=data) + + +async def call(*args, shell=False): + if shell: + ret = await asyncio.create_subprocess_shell(' '.join(args), stdout=asyncio.subprocess.PIPE) + else: + ret = await asyncio.create_subprocess_exec(*args, stdout=asyncio.subprocess.PIPE) + out,err = await ret.communicate() + if ret.returncode: + raise Exception(f'call failed: return code {ret.returncode}') + return out + + +@dc +class Entry: + name: str + path: str + size: int + is_dir: bool = False + is_link: bool = False + nfiles: int = 0 + percent_size: float = 0.0 + + +@dc +class DirEntry: + name: str + path: str + size: int + nfiles: int + children: list + + @classmethod + def from_entry(cls, e: Entry) -> Self: + if not e.is_dir: + raise Exception('is not a directory!') + return cls(e.name, e.path, e.size, e.nfiles, []) + + +class POSIXFileSystem: + def __init__(self, base_path): + self.base_path = Path(base_path) + + async def status(self): + try: + async with asyncio.timeout(5): + await call('/usr/bin/ls', str(self.base_path)) + except Exception as e: + return f'FAIL: {e}' + return 'OK' + + async def _get_meta(self, path: Path) -> Entry: + """Get recursive size and nfiles for a path""" + if not path.is_relative_to(self.base_path): + raise Exception('not relative to base path') + p = str(path) + async with asyncio.timeout(30): + stats = await asyncio.to_thread(path.stat) + is_dir = stat.S_ISDIR(stats.st_mode) + is_link = stat.S_ISLNK(stats.st_mode) + if is_dir: + size, nfiles = await asyncio.gather( + call('du', '-s', '-b', p, shell=True), + call(f'find "{p}" -type f | wc -l', shell=True) + ) + size = int(size.split()[0]) + nfiles = int(nfiles.strip()) + else: + size = stats.st_size + nfiles = 0 + return Entry(path.name, p, size, is_dir, is_link, nfiles) + + async def dir_entry(self, path: str) -> DirEntry: + """Get directory contents""" + fullpath = self.base_path / path.lstrip('/') + if not fullpath.is_dir(): + raise Exception('not a directory!') + + tasks = [] + async with asyncio.TaskGroup() as tg: + for child in fullpath.iterdir(): + tasks.append(tg.create_task(self._get_meta(child))) + + ret = DirEntry.from_entry(await self._get_meta(fullpath)) + for task in tasks: + r = await task + r.percent_size = r.size*100.0/ret.size + ret.children.append(r) + ret.children.sort(key=lambda r: r.name) + + return ret + + +class CephFileSystem(POSIXFileSystem): + async def _get_meta(self, path: Path) -> Entry: + """Get recursive size and nfiles for a path""" + if not path.is_relative_to(self.base_path): + raise Exception('not relative to base path') + p = str(path) + async with asyncio.timeout(30): + stats = await asyncio.to_thread(path.stat) + is_dir = stat.S_ISDIR(stats.st_mode) + is_link = stat.S_ISLNK(stats.st_mode) + if is_dir: + size, nfiles = await asyncio.gather( + call('/usr/bin/getfattr', '-n', 'ceph.dir.rbytes', p), + call('/usr/bin/getfattr', '-n', 'ceph.dir.rfiles', p) + ) + size = int(size.split('=')[-1].strip('" \n')) + nfiles = int(nfiles.split('=')[-1].strip('" \n')) + else: + size = stats.st_size + nfiles = 0 + return Entry(path.name, p, size, is_dir, is_link, nfiles) + + +class Server: + def __init__(self, s3_override=None): + static_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static') + template_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') + + default_config = { + 'HOST': 'localhost', + 'PORT': 8080, + 'DEBUG': False, + 'MAX_BODY_SIZE': 10**9, + 'CI_TESTING': '', + } + config = from_environment(default_config) + + kwargs = {} + if config['CI_TESTING']: + cwd = os.path.abspath(config['CI_TESTING']) + kwargs['filesystems'] = { + cwd: POSIXFileSystem(cwd), + } + else: + kwargs['filesystems'] = { + '/data/ana': CephFileSystem('/data/ana'), + '/data/user': CephFileSystem('/data/user'), + } + + server = RestServer( + static_path=static_path, + template_path=template_path, + debug=config['DEBUG'], + max_body_size=config['MAX_BODY_SIZE'], + ) + + server.add_route('/', Main, kwargs) + # handle moving up gracefully + if config['CI_TESTING']: + server.add_route(cwd, Main, kwargs) + else: + server.add_route('/data', Main, kwargs) + server.add_route('/healthz', Health, kwargs) + server.add_route(r'(.*)', Details, kwargs) + + server.startup(address=config['HOST'], port=config['PORT']) + + self.server = server + + async def start(self): + pass + + async def stop(self): + await self.server.stop() diff --git a/cephfs_disk_usage/static/apple-touch-icon.png b/cephfs_disk_usage/static/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..11e1a03ff0e5057b5fbc30b56f596a8f51a1720f GIT binary patch literal 8413 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 literal 0 HcmV?d00001 diff --git a/cephfs_disk_usage/static/boilerplate.css b/cephfs_disk_usage/static/boilerplate.css new file mode 100644 index 0000000..9fba625 --- /dev/null +++ b/cephfs_disk_usage/static/boilerplate.css @@ -0,0 +1,360 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} + +/* ========================================================================== + Browser Upgrade Prompt + ========================================================================== */ + +.browserupgrade { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} \ No newline at end of file diff --git a/cephfs_disk_usage/static/boilerplate.js b/cephfs_disk_usage/static/boilerplate.js new file mode 100644 index 0000000..3ea82f6 --- /dev/null +++ b/cephfs_disk_usage/static/boilerplate.js @@ -0,0 +1,23 @@ +// https://html5boilerplate.com/ +// Avoid `console` errors in browsers that lack a console. +(function() { + var method; + var noop = function () {}; + var methods = [ + 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', + 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', + 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', + 'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn' + ]; + var length = methods.length; + var console = (window.console = window.console || {}); + + while (length--) { + method = methods[length]; + + // Only stub undefined methods. + if (!console[method]) { + console[method] = noop; + } + } +}()); \ No newline at end of file diff --git a/cephfs_disk_usage/static/favicon.png b/cephfs_disk_usage/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..14df2fd4dfe3a269e54aaeb064b9b04c777d3956 GIT binary patch literal 3084 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 O0000div { + margin: 1em 1em; +} +article div.entry { + display: flex; + margin: .5em 0; +} +article div.entry div { + display: inline-block; + margin-left: .5em; +} +article div.entry div.name { + width: 20em; +} +article div.entry div.type { + width: 7em; +} +article div.entry div.value { + width: 7em; + text-align: right; +} +article div.entry div.graph { + width: 7em; + text-align: right; + margin-right: 1em; + position: relative; +} +article div.entry div.graph .text, article div.entry div.graph .text_small { + position: absolute; + width: 100%; + margin: 0; + z-index: 1; +} +article div.entry div.graph .text { + right: 0; + color: white; +} +article div.entry div.graph .fill { + background-color: green; + margin:0; + padding:0; + height: 100%; + z-index: 0; +} +article div.header { + font-weight: bold; +} + +footer { + margin-top: 2em; +} + +div.articles { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} +article.main { + max-width: 49%; + min-width: 29em; +} \ No newline at end of file diff --git a/cephfs_disk_usage/templates/base.html b/cephfs_disk_usage/templates/base.html new file mode 100644 index 0000000..1ed9499 --- /dev/null +++ b/cephfs_disk_usage/templates/base.html @@ -0,0 +1,30 @@ + + + + +{% block title %}{% end %}Disk Usage + + + + + + + + + +
+
+

+
+
{% block main %}{% end %}
+
+

For help, email help at icecube.wisc.edu

+ +
+
+ +{% block scripts %}{% end %} + + diff --git a/cephfs_disk_usage/templates/details.html b/cephfs_disk_usage/templates/details.html new file mode 100644 index 0000000..06d3f21 --- /dev/null +++ b/cephfs_disk_usage/templates/details.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block main %} +

Disk Usage Browser

+
+

Path: {{ data.path }}

+{% module Template('piechart.html', data=data) %} +{% module Template('table.html', data=data, up=True) %} +
+{% end %} + +{% block scripts %} + +{% module Template('piechart.js', data=data) %} +{% end %} \ No newline at end of file diff --git a/cephfs_disk_usage/templates/main.html b/cephfs_disk_usage/templates/main.html new file mode 100644 index 0000000..6905699 --- /dev/null +++ b/cephfs_disk_usage/templates/main.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% block main %} +

Disk Usage Browser

+
+{% for path in paths %} +
+

Path: {{ path }}

+ {% module Template('piechart.html', data=paths[path]) %} + {% module Template('table.html', data=paths[path], up=False) %} +
+{% end %} +
+{% end %} + +{% block scripts %} + +{% for path in paths %} +{% module Template('piechart.js', data=paths[path]) %} +{% end %} +{% end %} \ No newline at end of file diff --git a/cephfs_disk_usage/templates/piechart.html b/cephfs_disk_usage/templates/piechart.html new file mode 100644 index 0000000..828b1e1 --- /dev/null +++ b/cephfs_disk_usage/templates/piechart.html @@ -0,0 +1 @@ +
diff --git a/cephfs_disk_usage/templates/piechart.js b/cephfs_disk_usage/templates/piechart.js new file mode 100644 index 0000000..7a0c5be --- /dev/null +++ b/cephfs_disk_usage/templates/piechart.js @@ -0,0 +1,21 @@ +{% autoescape None %} + + \ No newline at end of file diff --git a/cephfs_disk_usage/templates/table.html b/cephfs_disk_usage/templates/table.html new file mode 100644 index 0000000..3f53ade --- /dev/null +++ b/cephfs_disk_usage/templates/table.html @@ -0,0 +1,31 @@ +
+ {% if up %} + << Up
+ {% end %} +
+
Name
+ {% if up %}
Type
{% end %} +
Size (GB)
+ {% if up %}
Size %
{% end %} +
+ {% for row in data.children %} +
+
{% if row.is_dir %}{{ row.name }}{% else %}{{ row.name }}{% end %}
+ {% if up %}
{% if row.is_dir %}dir{% elif row.is_link %}link{% else %}file{% end %}
{% end %} +
{{ '{:.2f}'.format(row.size/10**9) }}
+ {% if up %}
+ {% if row.percent_size < 20 %} +
{{ '{:.2f}'.format(row.percent_size) }}
+ {% else %} +
{{ '{:.2f}'.format(row.percent_size) }}
+ {% end %} +
+
{% end %} +
+ {% end %} +
+
Total
+ {% if up %}
{% end %} +
{{ '{:.2f}'.format(data.size/10**9) }}
+
+
diff --git a/setup.cfg b/setup.cfg index 26d8513..50e0c73 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,8 +1,8 @@ [wipac:cicd_setup_builder] -python_min = 3.10 +python_min = 3.11 [metadata] # generated by wipac:cicd_setup_builder: name, version -name = cephfs_disk_usage +name = cephfs-disk-usage version = attr: cephfs_disk_usage.__version__ [semantic_release] # fully-generated by wipac:cicd_setup_builder @@ -18,9 +18,16 @@ branch = main [options] # generated by wipac:cicd_setup_builder: python_requires, packages install_requires = wipac-rest-tools -python_requires = >=3.10, <3.12 +python_requires = >=3.11, <3.12 packages = find: +[options.extras_require] +tests = + flake8 + pytest + pytest-asyncio + pytest-mock + [options.package_data] # generated by wipac:cicd_setup_builder: '*' * = py.typed @@ -35,3 +42,9 @@ exclude = example examples +[flake8] +ignore = E203,E226,E228,E231,E501,W503,W504 + +[tool:pytest] +asyncio_mode = auto + diff --git a/setupenv.sh b/setupenv.sh new file mode 100755 index 0000000..fb6f80c --- /dev/null +++ b/setupenv.sh @@ -0,0 +1,6 @@ +#!/bin/sh +unset PYTHONPATH +python3 -m virtualenv -p python3 env +echo "unset PYTHONPATH" >> env/bin/activate +. env/bin/activate +pip install -e .[tests]