diff --git a/pyproject.toml b/pyproject.toml index 2c881c7..7721229 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,43 @@ cache-dir = ".tox/.ruff_cache" line-length = 140 src = ["src", "tests"] +[tool.ruff.lint] +extend-select = [ +# "A", # flake8-builtins +# "ARG", # flake8-unused-arguments + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "C90", # mccabe +# "D", # pydocstyle + "DTZ", # flake8-datetimez + "E", # pycodestyle errors + "ERA", # eradicate + "EXE", # flake8-executable + "F", # pyflakes + "FLY", # flynt + "G", # flake8-logging-format +# "I", # isort + "INT", # flake8-gettext + "PGH", # pygrep-hooks + "PIE", # flake8-pie +# "PT", # flake8-pytest +# "PYI", # flake8-pyi +# "Q", # flake8-quotes +# "RSE", # flake8-raise +# "RET", # flake8-return +## "RUF", # ruff-specific +## "S", # flake8-bandit +## "SIM", # flake8-simplify +## "SLF", # flake8-self +# "SLOT", # flake8-slots +# "T10", # flake8-debugger +# "TID", # flake8-tidy-imports +# "TCH", # flake8-type-checking +## "TD", # flake8-todos +## "TRY", # tryceratops +# "W", # pycodestyle warnings +] + [tool.ruff.lint.isort] order-by-type = false diff --git a/src/runez/_inspect.py b/src/runez/_inspect.py index e236331..664971d 100644 --- a/src/runez/_inspect.py +++ b/src/runez/_inspect.py @@ -5,10 +5,7 @@ def simple_inspection(): - return dict( - version=".".join(str(s) for s in sys.version_info[:3]), - machine=platform.machine(), - ) + return {"version": ".".join(str(s) for s in sys.version_info[:3]), "machine": platform.machine()} if __name__ == "__main__": diff --git a/src/runez/click.py b/src/runez/click.py index 5546931..7a77509 100644 --- a/src/runez/click.py +++ b/src/runez/click.py @@ -397,9 +397,9 @@ def __init__(self, attrs): def _get_values(self, value): value = flattened(value, split=self.split) values = [t.partition("=") for t in value if t] - values = dict((k, v) for k, _, v in values) + values = {k: v for k, _, v in values} if self.prefix: - values = dict((affixed(k, prefix=self.prefix), v) for k, v in values.items()) + values = {affixed(k, prefix=self.prefix): v for k, v in values.items()} return values diff --git a/src/runez/colors/terminal.py b/src/runez/colors/terminal.py index e1eff3b..ec79ed5 100644 --- a/src/runez/colors/terminal.py +++ b/src/runez/colors/terminal.py @@ -125,7 +125,7 @@ def named_triplet(self): """Triplet of named bg, fg and style-s""" bg = NamedColors( cls=AnsiColor, - params=dict(ansi=self.ansi, flavor=self.flavor), + params={"ansi": self.ansi, "flavor": self.flavor}, black=-0x000001, blue=-0x0000FF, brown=-0xA52A2A, @@ -140,7 +140,7 @@ def named_triplet(self): ) fg = NamedColors( cls=AnsiColor, - params=dict(ansi=self.ansi, flavor=self.flavor), + params={"ansi": self.ansi, "flavor": self.flavor}, black=0x000000, blue=0x0000FF, brown=0x850A0A, diff --git a/src/runez/conftest.py b/src/runez/conftest.py index 6bf9b40..263cef4 100644 --- a/src/runez/conftest.py +++ b/src/runez/conftest.py @@ -315,7 +315,7 @@ def test_my_cli(cli): if r.failed: msg = "%s --help failed" % runez.short(script) logging.error("%s\n%s", msg, r.full_output) - assert False, msg + raise AssertionError(msg) def run(self, *args, exe=None, main=UNSET, trace=UNSET): """ @@ -431,13 +431,11 @@ def expect_messages(self, *expected, **kwargs): for message in expected: if message[0] == "!": m = self.match(message[1:], **kwargs) - if m: - assert False, "Unexpected match in output: %s" % m + assert not m, "Unexpected match in output: %s" % m else: m = self.match(message, **kwargs) - if not m: - assert False, "Not seen in output: %s" % message + assert m, "Not seen in output: %s" % message def expect_success(self, args, *expected, **kwargs): spec = RunSpec() @@ -499,15 +497,11 @@ def _run_main(self, main, args): return result - if isinstance(main, str): - script = self._resolved_script(main) - if not script: - assert False, "Can't find script '%s', invalid main" % script - - r = runez.run(sys.executable, script, *args, fatal=False) - return ClickWrapper(r.output, r.error, r.exit_code, r.exc_info) - - assert False, "Can't invoke invalid main: %s" % main + assert isinstance(main, str), "Can't invoke invalid main: %s" % main + script = self._resolved_script(main) + assert script, "Can't find script '%s', invalid main" % main + r = runez.run(sys.executable, script, *args, fatal=False) + return ClickWrapper(r.output, r.error, r.exit_code, r.exc_info) class RunSpec(Slotted): diff --git a/src/runez/convert.py b/src/runez/convert.py index ae84b53..3e8bf40 100644 --- a/src/runez/convert.py +++ b/src/runez/convert.py @@ -491,5 +491,4 @@ def parsed_line(self, line): data[current.name].append(text) m = self.regex.search(line, wend + 1) - data = dict((k, joined(v)) for k, v in data.items()) - return data + return {k: joined(v) for k, v in data.items()} diff --git a/src/runez/date.py b/src/runez/date.py index 24a5871..bd752f2 100644 --- a/src/runez/date.py +++ b/src/runez/date.py @@ -67,7 +67,7 @@ def dst(self, dt): UTC = timezone(datetime.timedelta(0), "UTC") -NAMED_TIMEZONES = dict(Z=UTC, UTC=UTC) +NAMED_TIMEZONES = {"Z": UTC, "UTC": UTC} def date_from_epoch(epoch, in_ms=None): @@ -358,7 +358,6 @@ def to_seconds(duration): v = m.group(1) seconds = to_seconds(duration.replace(v, "")) - # v = v.strip() if v.endswith("w"): seconds += int(v[:-1], 0) * SECONDS_IN_ONE_DAY * 7 @@ -432,7 +431,7 @@ def _date_from_text(text, epocher, tz=UNSET): return None - # _, number, _, _, y, m, d, _, hh, mm, ss, sf, _, tz, _, _ = match.groups() + # Groups: _, number, _, _, y, m, d, _, hh, mm, ss, sf, _, tz, _, _ components = match.groups() if components[1]: return epocher(_float_from_text(components[1], lenient=True), tz=tz) diff --git a/src/runez/file.py b/src/runez/file.py index ddcbf05..bde8d3d 100644 --- a/src/runez/file.py +++ b/src/runez/file.py @@ -213,7 +213,7 @@ def ini_to_dict(path, keep_empty=False, fatal=False, logger=False): section[key] = value if not keep_empty: - result = dict((k, v) for k, v in result.items() if k and v) + result = {k: v for k, v in result.items() if k and v} return result diff --git a/src/runez/http.py b/src/runez/http.py index 5e7a209..f118582 100644 --- a/src/runez/http.py +++ b/src/runez/http.py @@ -370,7 +370,7 @@ def stop(self): def response_for_url(self, method, url): spec = self.specs.get(url) if spec is None: - return MockResponse(404, dict(message="Default status code 404")) + return MockResponse(404, {"message": "Default status code 404"}) if isinstance(spec, BaseException): raise spec @@ -1025,7 +1025,7 @@ def _get_response(self, method, url, fatal, logger, dryrun=False, state=None, ac absolute_url = self.full_url(url) message = "%s %s" % (action or method, absolute_url) if _R.hdry(dryrun, logger, message): - return RestResponse(method, absolute_url, MockResponse(200, dict(message="dryrun %s" % message))) + return RestResponse(method, absolute_url, MockResponse(200, {"message": "dryrun %s" % message})) cache_key = cache_expire = None if self.cache_wrapper is not None: diff --git a/src/runez/inspector.py b/src/runez/inspector.py index 4b1ca20..a30af37 100644 --- a/src/runez/inspector.py +++ b/src/runez/inspector.py @@ -59,7 +59,7 @@ def foo(...): import pkgutil imported = [] - for loader, module_name, _ in pkgutil.walk_packages([caller.folder], prefix="%s." % caller.package_name): + for _, module_name, _ in pkgutil.walk_packages([caller.folder], prefix="%s." % caller.package_name): if _should_auto_import(module_name, skip): importlib.import_module(module_name) imported.append(module_name) diff --git a/src/runez/logsetup.py b/src/runez/logsetup.py index 4c63c24..ea4fb93 100644 --- a/src/runez/logsetup.py +++ b/src/runez/logsetup.py @@ -526,7 +526,7 @@ def should_log_to_file(self): return bool(self.locations) def _props(self, **additional): - r = dict(argv=self.argv, pid=self.pid) + r = {"argv": self.argv, "pid": self.pid} r.update(self.to_dict()) r.update(additional) return r @@ -702,7 +702,7 @@ class LogManager: # Spec defines how logs should be setup() # Best way to provide your spec is via: runez.log.setup(), for example: - # runez.log.setup(rotate="size:50m") + # >>> runez.log.setup(rotate="size:50m") spec = LogSpec(_default_spec) # Thread-local / global context @@ -950,7 +950,7 @@ def is_using_format(cls, markers, used_formats=None): return any(marker in used_formats for marker in flattened(markers, split=" ")) @classmethod - def enable_faulthandler(cls, signum=getattr(signal, "SIGUSR1", None)): + def enable_faulthandler(cls, signum=UNSET): """Enable dumping thread stack traces when specified signals are received, similar to java's handling of SIGQUIT Note: this must be called from the surviving process in case of daemonization. @@ -958,6 +958,9 @@ def enable_faulthandler(cls, signum=getattr(signal, "SIGUSR1", None)): Args: signum (int | None): Signal number to register for full thread stack dump (use None to disable) """ + if signum is UNSET: + signum = getattr(signal, "SIGUSR1", None) + with cls._lock: if not signum: cls._disable_faulthandler() @@ -968,8 +971,8 @@ def enable_faulthandler(cls, signum=getattr(signal, "SIGUSR1", None)): cls.faulthandler_signum = signum dump_file = cls.file_handler.stream - faulthandler.enable(file=dump_file, all_threads=True) # noqa - faulthandler.register(signum, file=dump_file, all_threads=True, chain=False) # noqa + faulthandler.enable(file=dump_file, all_threads=True) + faulthandler.register(signum, file=dump_file, all_threads=True, chain=False) @classmethod def override_spec(cls, **settings): @@ -1055,10 +1058,10 @@ def _props(cls): def _auto_enable_progress_handler(cls): if cls.progress.is_running: if ProgressHandler not in logging.root.handlers: - logging.root.handlers.append(ProgressHandler) # noqa + logging.root.handlers.append(ProgressHandler) elif ProgressHandler in logging.root.handlers: - logging.root.handlers.remove(ProgressHandler) # noqa + logging.root.handlers.remove(ProgressHandler) @classmethod def _update_used_formats(cls): @@ -1292,7 +1295,7 @@ def _formatted_text(text, props, strict=False, max_depth=3): if not max_depth or not isinstance(max_depth, int) or max_depth <= 0: return text - result = dict((k, _format_recursive(k, v, definitions, max_depth)) for k, v in definitions.items()) + result = {k: _format_recursive(k, v, definitions, max_depth) for k, v in definitions.items()} return text.format(**result) diff --git a/src/runez/program.py b/src/runez/program.py index 270b803..c69c4f7 100644 --- a/src/runez/program.py +++ b/src/runez/program.py @@ -85,7 +85,7 @@ def cmd_basename(self): @cached_property def ps_follow(self): - return dict(tmux=("tmux", "display-message", "-p", "#{client_pid}")) + return {"tmux": ("tmux", "display-message", "-p", "#{client_pid}")} @cached_property def followed_parent(self): @@ -560,7 +560,7 @@ def require_installed(program, instructions=None, platform=None): """ if which(program) is None: if not instructions: - instructions = dict(macos="run: `brew install {program}`", linux="run: `apt install {program}`") + instructions = {"macos": "run: `brew install {program}`", "linux": "run: `apt install {program}`"} if isinstance(instructions, dict): instructions = _install_instructions(instructions, platform or SYS_INFO.platform_id.platform) diff --git a/src/runez/pyenv.py b/src/runez/pyenv.py index 85dfc45..4acdcb4 100644 --- a/src/runez/pyenv.py +++ b/src/runez/pyenv.py @@ -255,7 +255,7 @@ def ls_pypi(cls, package_name, client=None, index=None, source=None, fatal=False @classmethod def _versions_from_pypi(cls, releases, source=None): if isinstance(releases, dict): - for v, infos in releases.items(): + for _, infos in releases.items(): for info in infos: if not info.get("yanked"): size = info.get("size") @@ -497,7 +497,7 @@ def find_python(self, spec): def _find_python(self, spec): if isinstance(spec, str): - if spec.startswith("~") or spec.startswith(".") or "/" in spec or os.path.exists(spec): + if spec.startswith(("~", ".", "/")) or "/" in spec or os.path.exists(spec): return PythonInstallation.from_path(Path(resolved_path(spec)), short_name=short(spec)) elif spec == "invoker": @@ -715,7 +715,7 @@ def local_parts(self): v = getattr(self, "_local_parts", None) if v is None: v = self.local_part.split(".") - setattr(self, "_local_parts", v) + self._local_parts = v return v diff --git a/src/runez/render.py b/src/runez/render.py index a56479b..e438f68 100644 --- a/src/runez/render.py +++ b/src/runez/render.py @@ -6,19 +6,19 @@ from runez.system import SYS_INFO, UNSET, wcswidth -NAMED_BORDERS = dict( - ascii="rstgrid,t:+++=,m:+++-", - compact="c: ,h: -", - colon="c: : ,h: : -", - dots="t:....,b::::.,c::::,h:.:..", - empty="", - framed="t:┍┯┑━,m:┝┿┥━,b:┕┷┙━,c:│││,h:╞╪╡═", - github="h:-|--,c:|||", - mysql="t:+++-,b:+++-,c:|||", - reddit="h:-|--,c: | ", - rst="t: ==,b: ==,c: ", - rstgrid="mysql,h:+++=", -) +NAMED_BORDERS = { + "ascii": "rstgrid,t:+++=,m:+++-", + "compact": "c: ,h: -", + "colon": "c: : ,h: : -", + "dots": "t:....,b::::.,c::::,h:.:..", + "empty": "", + "framed": "t:┍┯┑━,m:┝┿┥━,b:┕┷┙━,c:│││,h:╞╪╡═", + "github": "h:-|--,c:|||", + "mysql": "t:+++-,b:+++-,c:|||", + "reddit": "h:-|--,c: | ", + "rst": "t: ==,b: ==,c: ", + "rstgrid": "mysql,h:+++=", +} class Align: @@ -138,7 +138,7 @@ def set_pad(self, value): self.pad = to_int(value) def _get_defaults(self): - return dict(c=_PTBorderChars(), pad=1) + return {"c": _PTBorderChars(), "pad": 1} def _values_from_string(self, text): values = {} @@ -192,7 +192,7 @@ def formatted(self, text): @staticmethod def merged(*chain): - values = dict(align=Align.left) + values = {"align": Align.left} for item in chain: if item is not None: values.update(item.to_dict()) @@ -472,7 +472,7 @@ def _values_from_object(self, obj): obj += [""] * missing first, mid, last, h = obj - return dict(first=first, mid=mid, last=last, h=h) + return {"first": first, "mid": mid, "last": last, "h": h} return super()._values_from_object(obj) diff --git a/src/runez/schema.py b/src/runez/schema.py index a76dfc8..6ff0e41 100644 --- a/src/runez/schema.py +++ b/src/runez/schema.py @@ -235,7 +235,7 @@ def _problem(self, value): return "value: %s" % problem def _converted(self, value): - return dict((self.key.converted(k), self.value.converted(v)) for k, v in value.items()) + return {self.key.converted(k): self.value.converted(v) for k, v in value.items()} class Enum(Any): @@ -358,7 +358,7 @@ def to_dict(self): Returns: (dict): This object serialized to a dict """ - return dict((name, getattr(self, name)) for name in self.meta.attributes) + return {name: getattr(self, name) for name in self.meta.attributes} def set_from_dict(self, data, source=None): """ diff --git a/src/runez/serialize.py b/src/runez/serialize.py index b1fa6d5..d2491fc 100644 --- a/src/runez/serialize.py +++ b/src/runez/serialize.py @@ -40,7 +40,7 @@ def with_behavior(strict=UNSET, extras=UNSET, hook=UNSET): Returns: (type): Internal temp class (compatible with `Serializable` metaclass) indicating how to handle Serializable type checking """ - return BaseMetaInjector("_MBehavior", tuple(), {"behavior": DefaultBehavior(strict=strict, extras=extras, hook=hook)}) + return BaseMetaInjector("_MBehavior", (), {"behavior": DefaultBehavior(strict=strict, extras=extras, hook=hook)}) def is_serializable_descendant(base): @@ -206,14 +206,13 @@ def json_sanitized(value, stringify=stringified, dt=str, none=False): value = sorted(value) if isinstance(value, dict): - return dict( - ( - json_sanitized(none if k is None and isinstance(none, str) else k, stringify=stringify, dt=dt, none=none), - json_sanitized(v, stringify=stringify, dt=dt, none=none), + return { + json_sanitized(none if k is None and isinstance(none, str) else k, stringify=stringify, dt=dt, none=none): json_sanitized( + v, stringify=stringify, dt=dt, none=none ) for k, v in value.items() if none or (k is not None and v is not None) - ) + } if is_iterable(value): return [json_sanitized(v, stringify=stringify, dt=dt, none=none) for v in value] @@ -376,7 +375,7 @@ def __init__(self, cls, mbehavior=None): self.attributes[key] = schema_type by_type[schema_type.__class__].append(key) - self._by_type = dict((k, sorted(v)) for k, v in by_type.items()) # Sorted to make things deterministic + self._by_type = {k: sorted(v) for k, v in by_type.items()} # Sorted to make things deterministic if self.attributes: SerializableDescendants.register(self) @@ -623,7 +622,7 @@ def to_dict(self, stringify=stringified, dt=str, none=False): Returns: (dict): This object serialized to a dict """ - raw = dict((name, getattr(self, name)) for name in self._meta.attributes) + raw = {name: getattr(self, name) for name in self._meta.attributes} return json_sanitized(raw, stringify=stringify, dt=dt, none=none) diff --git a/src/runez/system.py b/src/runez/system.py index 1feed84..1ac7f7f 100644 --- a/src/runez/system.py +++ b/src/runez/system.py @@ -14,7 +14,6 @@ import unicodedata from io import StringIO - ABORT_LOGGER = logging.error LOG = logging.getLogger("runez") SYMBOLIC_TMP = "" @@ -1178,7 +1177,7 @@ def _seed(self): """Seed initial fields""" defaults = self._get_defaults() if not isinstance(defaults, dict): - defaults = dict((k, defaults) for k in self.__slots__) + defaults = {k: defaults for k in self.__slots__} for name in self.__slots__: value = getattr(self, name, defaults.get(name)) @@ -1235,7 +1234,7 @@ def _values_from_string(self, text): def _values_from_object(self, obj): """dict: Optional hook to allow descendants to extract key/value pairs from an object""" if obj is not None: - return dict((k, getattr(obj, k, UNSET)) for k in self.__slots__) + return {k: getattr(obj, k, UNSET) for k in self.__slots__} class DevInfo: @@ -1319,7 +1318,7 @@ class PlatformId: subsystem: str = None # Example: libc, musl (empty for macos/windows for now) default_subsystem = None # Can this be auto-detected? (currently: users can optionally provide this, by setting this class field) - platform_archive_type = dict(linux="tar.gz", macos="tar.gz", windows="zip") + platform_archive_type = {"linux": "tar.gz", "macos": "tar.gz", "windows": "zip"} sys_include = None # Most standard system include dirs, if any rx_base_path = None # Regex identifying "base" libraries (present on any declination of this system) @@ -1562,6 +1561,7 @@ def invoker_python(self): """The python that is either currently running us, or that created the venv we're running from""" import platform from pathlib import Path + from runez.pyenv import PythonInstallation, PythonSimpleInspection path = Path(sys.executable) @@ -2017,7 +2017,7 @@ def rx_date(self): r"([Tt \t](\d\d?):(\d\d?):(\d\d?)(\.\d*)?" r"([ \t]*(Z|[A-Z]{3}|[+-]\d\d?(:(\d\d?))?))?)?)" ) - return re.compile(r"^\s*(%s)\s*$" % "|".join((base_number, base_date))) + return re.compile(rf"^\s*({base_number}|{base_date})\s*$") @cached_property def rx_duration(self): diff --git a/tests/secondary/test_import.py b/tests/secondary/test_import.py index 3ef8d47..160edd8 100644 --- a/tests/secondary/test_import.py +++ b/tests/secondary/test_import.py @@ -2,7 +2,7 @@ Make sure 'from runez import *' (not recommended!) works correctly """ -from runez import * # noqa +from runez import * # noqa: F403 def test_import(): @@ -10,4 +10,4 @@ def test_import(): assert len(names) > 10 assert "short" in names - assert short("foo") == "foo" # noqa + assert short("foo") == "foo" # noqa: F405 diff --git a/tests/test_click.py b/tests/test_click.py index b596821..779ef61 100644 --- a/tests/test_click.py +++ b/tests/test_click.py @@ -38,7 +38,7 @@ def my_group(debug): config = runez.config.CONFIG cd = config.get("g.cd") if cd: - logging.info("Changed folder to %s" % runez.short(cd)) + logging.info("Changed folder to %s", runez.short(cd)) runez.ensure_folder(cd) os.chdir(cd) @@ -56,9 +56,7 @@ def echo(text): This part will be an {placeholder} """ text = " ".join(text) - if text == "AssertionError": - assert False, "oops" - + assert text != "AssertionError", "oops" msg = "%s, color: %s, %s values, g.a=%s" % (text, runez.color.is_coloring(), len(runez.config.CONFIG), runez.config.get("g.a")) msg += ", debug: %s, dryrun: %s, log: %s" % (runez.log.debug, runez.DRYRUN, runez.log.spec.file_location) print(msg) @@ -276,7 +274,7 @@ def test_settings(): assert len(s) == 3 assert s["epilog"] == "some epilog" assert s["foo"] == "bar" - assert s["context_settings"] == dict(help_option_names=["-h", "--help"], max_content_width=140) + assert s["context_settings"] == {"help_option_names": ["-h", "--help"], "max_content_width": 140} s = runez.click.settings(help="-h --help --explain") assert s["context_settings"]["help_option_names"] == ["-h", "--help", "--explain"] diff --git a/tests/test_config.py b/tests/test_config.py index 277d7a2..0713254 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -51,7 +51,7 @@ def test_no_implementation(): assert config.get("anything") is None with pytest.raises(ValueError): - config.add(object()) # noqa + config.add(object()) assert str(config) == "empty" diff --git a/tests/test_date.py b/tests/test_date.py index 5c4ddd8..faa7b98 100644 --- a/tests/test_date.py +++ b/tests/test_date.py @@ -12,7 +12,7 @@ def check_date(expected, dt): def test_date_formats(): - ref = dt(2019, 1, 16) + ref = datetime.date(2019, 1, 16) assert runez.to_date("2019-01-16") == ref assert runez.to_date(" 2019/01/16 ") == ref assert runez.to_date("01/16/2019") == ref @@ -32,7 +32,7 @@ def test_date_formats(): def test_elapsed(): d1 = datetime.date(2019, 9, 1) - dt27 = datetime.datetime(2019, 9, 1, second=27) + dt27 = dt(2019, 9, 1, second=27) assert runez.to_date(d1) is d1 assert runez.to_datetime(dt27) is dt27 @@ -41,22 +41,22 @@ def test_elapsed(): assert runez.elapsed(dt27, ended=d1) == -27 d2 = datetime.date(2019, 9, 2) - dt1 = datetime.datetime(2019, 9, 1) + dt1 = dt(2019, 9, 1) assert runez.elapsed(d1, ended=d2) == 86400 assert runez.elapsed(d2, ended=dt1) == -86400 - dt = runez.datetime_from_epoch(1567296012, tz=None) # Naive date will depend on timezone (ie: where this test runs) - assert dt.year == 2019 - assert dt.tzinfo is None - assert runez.to_datetime(dt) is dt + d3 = runez.datetime_from_epoch(1567296012, tz=None) # Naive date will depend on timezone (ie: where this test runs) + assert d3.year == 2019 + assert d3.tzinfo is None + assert runez.to_datetime(d3) is d3 check_date("2019-09-01 02:00:12 +02:00", runez.datetime_from_epoch(1567296012, tz=runez.timezone_from_text("0200", default=None))) check_date("2019-09-01 00:00:12 UTC", runez.datetime_from_epoch(1567296012, tz=runez.date.UTC)) check_date("2019-09-01 00:00:12 UTC", runez.datetime_from_epoch(1567296012000, tz=runez.date.UTC, in_ms=True)) with freeze_time("2019-09-01 00:00:12"): - assert runez.elapsed(datetime.datetime(2019, 9, 1, second=34)) == -22 - assert runez.elapsed(datetime.datetime(2019, 9, 1)) == 12 + assert runez.elapsed(dt(2019, 9, 1, second=34)) == -22 + assert runez.elapsed(dt(2019, 9, 1)) == 12 def test_epoch(): @@ -73,7 +73,7 @@ def test_epoch(): def test_represented_duration(): assert runez.represented_duration(None) == "None" assert runez.represented_duration("foo") == "foo" # noqa, verifiy non-duration left as-is... - assert runez.represented_duration(runez.UNSET) == "UNSET" # noqa + assert runez.represented_duration(runez.UNSET) == "UNSET" assert runez.represented_duration(0) == "0 seconds" assert runez.represented_duration(1) == "1 second" @@ -121,14 +121,14 @@ def test_timezone(monkeypatch): assert runez.timezone_from_text("+0100", default=None) != runez.date.UTC epoch = 1568332800 - assert runez.to_date(epoch) == dt(2019, 9, 13) + assert runez.to_date(epoch) == datetime.date(2019, 9, 13) assert runez.to_date(epoch) == runez.to_date(epoch * 1000) assert runez.to_datetime(epoch) == dt(2019, 9, 13, 0, 0, 0) epoch = 1568348000 check_date("2019-09-13 04:13:20 UTC", runez.datetime_from_epoch(epoch)) - assert runez.to_date(epoch) == dt(2019, 9, 13) + assert runez.to_date(epoch) == datetime.date(2019, 9, 13) assert runez.to_datetime(epoch) == dt(2019, 9, 13, 4, 13, 20) tz1 = runez.timezone(datetime.timedelta(seconds=12 * 60)) @@ -149,11 +149,8 @@ def test_timezone(monkeypatch): def dt(*args, **kwargs): - if len(args) == 3: - return datetime.date(*args) - - kwargs.setdefault("tzinfo", runez.date.DEFAULT_TIMEZONE) - return datetime.datetime(*args, **kwargs) + tzinfo = kwargs.pop("tzinfo", runez.date.DEFAULT_TIMEZONE) + return datetime.datetime(*args, tzinfo=tzinfo, **kwargs) def test_to_date(): @@ -174,16 +171,16 @@ def test_to_date(): assert d0 == d2 assert d1 == d2 - assert runez.to_date(d0) == dt(2019, 1, 2) + assert runez.to_date(d0) == datetime.date(2019, 1, 2) assert runez.to_datetime(None) is None assert runez.to_datetime("foo") is None assert runez.to_datetime(["foo"]) is None - assert runez.to_date("2019-01-02") == dt(2019, 1, 2) - assert runez.to_date("2019-01-02 00:01:00UTC") == dt(2019, 1, 2) - assert runez.to_date("2019-01-02 00:01:00 UTC") == dt(2019, 1, 2) - assert runez.to_date("2019-01-02 00:01:00 UTC ") == dt(2019, 1, 2) + assert runez.to_date("2019-01-02") == datetime.date(2019, 1, 2) + assert runez.to_date("2019-01-02 00:01:00UTC") == datetime.date(2019, 1, 2) + assert runez.to_date("2019-01-02 00:01:00 UTC") == datetime.date(2019, 1, 2) + assert runez.to_date("2019-01-02 00:01:00 UTC ") == datetime.date(2019, 1, 2) assert runez.to_datetime("2019-01-02") == dt(2019, 1, 2, 0, 0, 0) assert runez.to_datetime("2019-01-02 00:01:00 UTC") == dt(2019, 1, 2, 0, 1, 0) diff --git a/tests/test_file.py b/tests/test_file.py index a927654..9b80a8b 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -160,7 +160,7 @@ def test_ensure_folder(temp_folder, logged): assert not logged assert runez.touch("some-file", logger=None) == 1 - with pytest.raises(Exception): + with pytest.raises(runez.system.AbortException): runez.ensure_folder("some-file") assert "Can't create folder" in logged.pop() @@ -380,8 +380,8 @@ def test_pathlib(temp_folder): runez.ensure_folder(subfolder) assert subfolder.is_dir() - with pytest.raises(Exception): - runez.to_path("foo bar", no_spaces=Exception) + with pytest.raises(ValueError): + runez.to_path("foo bar", no_spaces=ValueError) with runez.CurrentFolder(subfolder, anchor=True): path = Path("foo") diff --git a/tests/test_http.py b/tests/test_http.py index 43cb400..e3da6c8 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -249,7 +249,7 @@ def dynamic_call(method, url): @EXAMPLE.mock( { "foo-bar": {"foo": "bar"}, # status 200 implied, payload is a dict - "bad-request": (400, dict(error="oops", msg="more info")), # status 400, with sample error + "bad-request": (400, {"error": "oops", "msg": "more info"}), # status 400, with sample error "server-crashed": (500, "failed"), # status 500, with optional content as well "dynamic-a": dynamic_call, # status and payload will come from function call "dynamic-b": dynamic_call, diff --git a/tests/test_inspector.py b/tests/test_inspector.py index b845098..39bb0dc 100644 --- a/tests/test_inspector.py +++ b/tests/test_inspector.py @@ -67,7 +67,7 @@ def needs_bar(self, msg): @AutoInstall("foo") def needs_foo(msg): - import foo # noqa + import foo # noqa: F401 return "OK: %s" % msg diff --git a/tests/test_logsetup.py b/tests/test_logsetup.py index 4429df9..3a320dd 100644 --- a/tests/test_logsetup.py +++ b/tests/test_logsetup.py @@ -263,10 +263,10 @@ def test_formatted_text(): assert _formatted_text("~/.cache/{foo}", {"foo": "bar"}) == os.path.expanduser("~/.cache/bar") # Verify that not all '{...}' text is considered a marker - props = dict(argv='{a} {"foo": "bar {a}"} {a}', a="{b}", b="b") + props = {"argv": '{a} {"foo": "bar {a}"} {a}', "a": "{b}", "b": "b"} assert _formatted_text(":: {argv} {a}", props) == ':: {a} {"foo": "bar {a}"} {a} b' - deep = dict(a="a", b="b", aa="{a}", bb="{b}", ab="{aa}{bb}", ba="{bb}{aa}", abba="{ab}{ba}", deep="{abba}") + deep = {"a": "a", "b": "b", "aa": "{a}", "bb": "{b}", "ab": "{aa}{bb}", "ba": "{bb}{aa}", "abba": "{ab}{ba}", "deep": "{abba}"} assert _formatted_text("{deep}", deep, max_depth=-1) == "{deep}" assert _formatted_text("{deep}", deep, max_depth=0) == "{deep}" assert _formatted_text("{deep}", deep, max_depth=1) == "{abba}" @@ -276,11 +276,11 @@ def test_formatted_text(): assert _formatted_text("{deep}", deep, max_depth=5) == "abba" assert _formatted_text("{deep}", deep, max_depth=6) == "abba" - recursive = dict(a="a{b}", b="b{c}", c="c{a}") + recursive = {"a": "a{b}", "b": "b{c}", "c": "c{a}"} assert _formatted_text("{a}", recursive) == "abc{a}" assert _formatted_text("{a}", recursive, max_depth=10) == "abcabcabca{b}" - cycle = dict(a="{b}", b="{a}") + cycle = {"a": "{b}", "b": "{a}"} assert _formatted_text("{a}", cycle, max_depth=0) == "{a}" assert _formatted_text("{a}", cycle, max_depth=1) == "{b}" assert _formatted_text("{a}", cycle, max_depth=2) == "{a}" @@ -578,7 +578,7 @@ def next_progress_line(progress_spinner): ts += 1 text = progress_spinner._state.get_line(ts) - setattr(progress_spinner, "_test_ts", ts) + progress_spinner._test_ts = ts return text diff --git a/tests/test_program.py b/tests/test_program.py index aa9b228..30abcc4 100644 --- a/tests/test_program.py +++ b/tests/test_program.py @@ -116,7 +116,7 @@ def test_capture(monkeypatch): logged.pop() # Test failure - with pytest.raises(Exception): + with pytest.raises(runez.system.AbortException): runez.run(CHATTER, "fail") assert "Run failed:" in logged.pop() diff --git a/tests/test_pyenv.py b/tests/test_pyenv.py index a73d807..b895255 100644 --- a/tests/test_pyenv.py +++ b/tests/test_pyenv.py @@ -51,7 +51,7 @@ def mk_python(basename, executable=True, content=None, machine=None): path = folder / ("python%s" % version.mm) if not content: - content = dict(version=str(version), machine=machine or runez.SYS_INFO.platform_id.arch) + content = {"version": str(version), "machine": machine or runez.SYS_INFO.platform_id.arch} if content == "failed": content = "echo failed\nexit 1" @@ -76,7 +76,7 @@ def test_depot(temp_folder, logged): mk_python("miniforge3-22.11.1-4/9.11.2") mk_python("pypy-9.8.7/9.8.7") mk_python("8.5.4", content="invalid content") - mk_python("8.5.5", content=dict(version="invalid-version")) + mk_python("8.5.5", content={"version": "invalid-version"}) mk_python("8.5.6", content="failed") mk_python("8.5.7", content="invalid-json") runez.symlink(".pyenv/versions/8.6.1", ".pyenv/versions/8.6", logger=None) diff --git a/tests/test_schema.py b/tests/test_schema.py index e046a20..19dfbf3 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -7,12 +7,12 @@ def test_any(): - any = Any() - assert any.problem(None) is None - assert any.problem("a") is None - assert any.problem(4) is None - assert any.problem([1, 2]) is None - assert any.problem(object()) is None + a = Any() + assert a.problem(None) is None + assert a.problem("a") is None + assert a.problem(4) is None + assert a.problem([1, 2]) is None + assert a.problem(object()) is None def test_boolean(): diff --git a/tests/test_serialize.py b/tests/test_serialize.py index ba7a1ca..adbc907 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -277,7 +277,7 @@ def test_sanitize(): # `none=True` disables any filtering assert runez.serialize.json_sanitized(sample, none=True) == sample - now = datetime.datetime.now() + now = datetime.datetime.now(tz=runez.date.UTC) assert runez.serialize.json_sanitized(now) == str(now) assert runez.serialize.json_sanitized(now, dt=None) is now assert runez.serialize.json_sanitized([now]) == [str(now)] diff --git a/tests/test_system.py b/tests/test_system.py index 75f5cc8..1e834d6 100644 --- a/tests/test_system.py +++ b/tests/test_system.py @@ -55,12 +55,12 @@ def on_log(message): assert not logged monkeypatch.setattr(runez.system.logging.root, "handlers", []) - with pytest.raises(Exception): + with pytest.raises(runez.system.AbortException): # logger is UNSET -> log failure runez.abort("oops") assert "oops" in logged.pop() - with pytest.raises(Exception) as exc: + with pytest.raises(runez.system.AbortException) as exc: # Message not logged, but part of raised exception runez.abort("oops", logger=None) assert "oops" in str(exc) @@ -516,9 +516,9 @@ def test_shortening(): assert runez.short([1, "b"]) == "[1, b]" assert runez.short((1, {"b": ["c", {"d", "e"}]})) == "(1, {b: [c, {d, e}]})" - complex = {"a \n b": [1, None, "foo \n ,", {"a2": runez.abort, "c": runez.Anchored}], None: datetime.date(2019, 1, 1)} - assert runez.short(complex) == "{None: 2019-01-01, a b: [1, None, foo ,, {a2: function 'abort', c: class runez.system.Anchored}]}" - assert runez.short(complex, size=32) == "{None: 2019-01-01, a b: [1, N..." + c = {"a \n b": [1, None, "foo \n ,", {"a2": runez.abort, "c": runez.Anchored}], None: datetime.date(2019, 1, 1)} + assert runez.short(c) == "{None: 2019-01-01, a b: [1, None, foo ,, {a2: function 'abort', c: class runez.system.Anchored}]}" + assert runez.short(c, size=32) == "{None: 2019-01-01, a b: [1, N..." assert runez.short(" some text ", size=32) == "some text" assert runez.short(" some text ", size=7) == "some..." @@ -697,7 +697,6 @@ def test_wcswidth(): assert runez.wcswidth("--\u05bf--") == 4 assert runez.wcswidth("café") == 4 assert runez.wcswidth("\u0410\u0488") == 1 - # assert runez.wcswidth("ᬓᬨᬮ᭄") == 4 assert runez.wcswidth("😊") == 2 assert runez.wcswidth("⚡") == 2 diff --git a/tests/test_testing.py b/tests/test_testing.py index 47b0dda..f8549ca 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -29,9 +29,7 @@ def sample_main(): # Raise a generic exception raise Exception("crashed: %s" % args[1:]) - if args[0] == "AssertionError": - assert False, "oops, something went wrong" - + assert args[0] != "AssertionError", "oops, something went wrong" if args[0] == "exit": # exit without explicit code sys.exit(" ".join(args[1:]))