diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 3459ccc..ab0efb7 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -16,12 +16,12 @@ jobs: name: Check style formatting steps: - uses: "actions/checkout@v2" - - uses: "actions/setup-python@v1" + - uses: "astral-sh/ruff-action@v3" with: - python-version: "3.13" - allow-prereleases: true - - run: python3 -m pip install black - - run: black . + args: "check --output-format=github" + - uses: "astral-sh/ruff-action@v3" + with: + args: "format --check --diff" pytest: runs-on: "ubuntu-latest" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 429d8bf..7ccdfd3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,10 @@ repos: - - repo: https://github.com/psf/black - rev: 25.1.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.14 hooks: - - id: black - args: - - --safe - - --quiet - files: ^((custom_components|tests)/.+)?[^/]+\.py$ + - id: ruff-check + args: [--fix, --show-fixes] + - id: ruff-format - repo: https://github.com/codespell-project/codespell rev: v2.4.1 hooks: @@ -16,27 +14,6 @@ repos: - --skip="./.*,*.csv,*.json" - --quiet-level=2 exclude_types: [csv, json] - - repo: https://github.com/pycqa/flake8 - rev: 7.3.0 - hooks: - - id: flake8 - additional_dependencies: - - flake8-docstrings==1.6.0 - - pydocstyle==6.1.1 - files: ^(custom_components|tests)/.+\.py$ - - repo: https://github.com/PyCQA/bandit - rev: 1.8.6 - hooks: - - id: bandit - args: - - --quiet - - --format=custom - - --configfile=tests/bandit.yaml - files: ^(custom_components|tests)/.+\.py$ - - repo: https://github.com/PyCQA/isort - rev: 6.0.1 - hooks: - - id: isort - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: diff --git a/custom_components/pyscript/jupyter_kernel.py b/custom_components/pyscript/jupyter_kernel.py index a402783..6b7e9fb 100644 --- a/custom_components/pyscript/jupyter_kernel.py +++ b/custom_components/pyscript/jupyter_kernel.py @@ -338,7 +338,6 @@ async def shell_handler(self, shell_socket, wire_msg): await self.send(self.iopub_socket, "status", content, parent_header=msg["header"]) if msg["header"]["msg_type"] == "execute_request": - content = { "execution_count": self.execution_count, "code": msg["content"]["code"], diff --git a/custom_components/pyscript/trigger.py b/custom_components/pyscript/trigger.py index 9fb45ac..e5ea31d 100644 --- a/custom_components/pyscript/trigger.py +++ b/custom_components/pyscript/trigger.py @@ -1055,7 +1055,6 @@ async def trigger_watch(self): """Task that runs for each trigger, waiting for the next trigger and calling the function.""" try: - if self.state_trigger is not None: self.state_trig_ident = set() if self.state_user_watch: @@ -1084,7 +1083,9 @@ async def trigger_watch(self): Event.notify_add(self.event_trigger[0], self.notify_q) if self.mqtt_trigger is not None: _LOGGER.debug("trigger %s adding mqtt_trigger %s", self.name, self.mqtt_trigger[0]) - await Mqtt.notify_add(self.mqtt_trigger[0], self.notify_q, encoding=self.mqtt_trigger_encoding) + await Mqtt.notify_add( + self.mqtt_trigger[0], self.notify_q, encoding=self.mqtt_trigger_encoding + ) if self.webhook_trigger is not None: _LOGGER.debug("trigger %s adding webhook_trigger %s", self.name, self.webhook_trigger[0]) Webhook.notify_add( diff --git a/pylintrc b/pylintrc index 3b78f42..cf38399 100644 --- a/pylintrc +++ b/pylintrc @@ -12,7 +12,6 @@ good-names=id,i,j,k,ex,Run,_,fp,T [MESSAGES CONTROL] # Reasons disabled: -# format - handled by black # locally-disabled - it spams too much # duplicate-code - unavoidable # cyclic-import - doesn't test if both import on load @@ -25,7 +24,6 @@ good-names=id,i,j,k,ex,Run,_,fp,T # too-many-ancestors - it's too strict. # wrong-import-order - isort guards this disable= - format, abstract-method, broad-except, cyclic-import, @@ -53,7 +51,20 @@ disable= no-value-for-parameter, unsubscriptable-object, wrong-import-order, - unidiomatic-typecheck + unidiomatic-typecheck, + # Covered by Ruff + format, + unused-import, + unused-variable, + undefined-variable, + used-before-assignment, + redefined-outer-name, + wildcard-import, + unused-wildcard-import, + missing-module-docstring, + missing-class-docstring, + missing-function-docstring, + empty-docstring enable= use-symbolic-message-instead diff --git a/pyproject.toml b/pyproject.toml index 8b9737b..1600081 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,78 @@ line-length = 109 #[tool.pytest.ini_options] #asyncio_mode = "auto" #asyncio_default_fixture_loop_scope = "function" + +[tool.ruff] +target-version = "py314" +line-length = 109 +src = ["custom_components", "tests"] +include = ["custom_components/**/*.py", "tests/**/*.py"] + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint.flake8-annotations] +ignore-fully-untyped = true + +[tool.ruff.lint.isort] +force-sort-within-sections = true +known-first-party = ["homeassistant", "tests", "custom_components.pyscript"] +combine-as-imports = true + +[tool.ruff.lint] +select = [ + "ANN", # flake8-annotations + "B", # flake8-bugbear + "D", # pydocstyle + "E", # pycodestyle + "F", # pyflakes + "FIX", # flake8-fixme + "G", # flake8-logging-format + "I", # isort + "PLC", #pylint convention + "RUF", # ruff-specific rules + "S", # flake8-bandit + "UP", # pyupgrade + "W", # pycodestyle +] +ignore = [ + "ANN003", # missing-type-kwargs + "ANN401", # any-type + "D202", # blank-line-after-function + "D203", # incorrect-blank-line-before-class + "D212", # multi-line-summary-first-line + "E501", # line-too-long + "UP037", # quoted-annotation + #pylint covered + "B012", # jump-statement-in-finally + "B904", # raise-without-from-inside-except + "PLC0415", # import-outside-top-level + "S102", # exec-builtin +] + + +[tool.ruff.lint.per-file-ignores] +"eval.py" = [ + "B007", # unused-loop-control-variable + "B009", # get-attr-with-constant + "B905", # zip-without-explicit-strict + "S506", # unsafe-yaml-load + "RUF005", #collection-literal-concatenation +] +"pyscript_builtins.py" = [ + "ANN", # flake8-annotations + "B", # flake8-bugbear + "D", # pydocstyle +] +"tests/*.py" = [ + "S101", # assert + "B011"# assert-false +] +#temporary +"__init__.py" = ["B007"] +"config_flow.py" = ["B007", "UP006", "UP035", "RUF013"] +"event.py" = ["RUF012"] +"jupyter_kernel.py" = ["RUF006", "S104"] +"mqtt.py" = ["RUF012"] +"trigger.py" = ["RUF012", "UP041"] +"webhook.py" = ["RUF012"] diff --git a/setup.cfg b/setup.cfg index f9c138f..30a174b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,43 +22,6 @@ addopts = --strict-markers --asyncio-mode=auto -[flake8] -exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build -doctests = True -# To work with Black -max-line-length = 109 -# E501: line too long -# W503: Line break occurred before a binary operator -# E203: Whitespace before ':' -# D202 No blank lines allowed after function docstring -# W504 line break after binary operator -# E231 missing whitespace after ':' -ignore = - E501, - W503, - E203, - D202, - W504 - E231 - -[isort] -# https://github.com/timothycrosley/isort -# https://github.com/timothycrosley/isort/wiki/isort-Settings -# splits long import on multiple lines indented by 4 spaces -multi_line_output = 3 -include_trailing_comma=True -force_grid_wrap=0 -use_parentheses=True -line_length=109 -indent = " " -# will group `import x` and `from x import` of the same module. -force_sort_within_sections = true -sections = FUTURE,STDLIB,INBETWEENS,THIRDPARTY,FIRSTPARTY,LOCALFOLDER -default_section = THIRDPARTY -known_first_party = homeassistant,tests -forced_separate = tests -combine_as_imports = true - [mypy] python_version = 3.13 ignore_errors = true diff --git a/tests/test_apps_modules.py b/tests/test_apps_modules.py index 4e7e37b..8e1a4ea 100644 --- a/tests/test_apps_modules.py +++ b/tests/test_apps_modules.py @@ -161,21 +161,17 @@ def glob_side_effect(path, recursive=None, root_dir=None, dir_fd=None, include_h return result conf = {"apps": {"world": {}}} - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob" - ) as mock_glob, patch("custom_components.pyscript.global_ctx.open", mock_open), patch( - "custom_components.pyscript.open", mock_open - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={"pyscript": conf} - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.os.path.isfile" - ) as mock_isfile: + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob") as mock_glob, + patch("custom_components.pyscript.global_ctx.open", mock_open), + patch("custom_components.pyscript.open", mock_open), + patch("homeassistant.config.load_yaml_config_file", return_value={"pyscript": conf}), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.os.path.isfile") as mock_isfile, + ): mock_isfile.side_effect = isfile_side_effect mock_glob.side_effect = glob_side_effect assert await async_setup_component(hass, "pyscript", {DOMAIN: conf}) diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 413a50e..269bd87 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -287,8 +287,9 @@ async def test_options_flow_user_no_change(hass, pyscript_bypass_setup): @pytest.mark.asyncio async def test_config_entry_reload(hass): """Test that config entry reload does not duplicate listeners.""" - with patch("homeassistant.config.load_yaml_config_file", return_value={}), patch( - "custom_components.pyscript.watchdog_start", return_value=None + with ( + patch("homeassistant.config.load_yaml_config_file", return_value={}), + patch("custom_components.pyscript.watchdog_start", return_value=None), ): result = await hass.config_entries.flow.async_init( DOMAIN, diff --git a/tests/test_decorator_errors.py b/tests/test_decorator_errors.py index d0f5051..4caf262 100644 --- a/tests/test_decorator_errors.py +++ b/tests/test_decorator_errors.py @@ -22,23 +22,20 @@ async def setup_script(hass, notify_q, now, source): Function.hass = None - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob", return_value=scripts - ), patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source)), patch( - "custom_components.pyscript.trigger.dt_now", return_value=now - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={} - ), patch( - "custom_components.pyscript.open", mock_open(read_data=source) - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.install_requirements", - return_value=None, + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob", return_value=scripts), + patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source)), + patch("custom_components.pyscript.trigger.dt_now", return_value=now), + patch("homeassistant.config.load_yaml_config_file", return_value={}), + patch("custom_components.pyscript.open", mock_open(read_data=source)), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch( + "custom_components.pyscript.install_requirements", + return_value=None, + ), ): assert await async_setup_component(hass, "pyscript", {DOMAIN: {}}) diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 8ea405b..8350500 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -22,23 +22,20 @@ async def setup_script(hass, notify_q, now, source): Function.hass = None - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob", return_value=scripts - ), patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source)), patch( - "custom_components.pyscript.trigger.dt_now", return_value=now - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={} - ), patch( - "custom_components.pyscript.open", mock_open(read_data=source) - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.install_requirements", - return_value=None, + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob", return_value=scripts), + patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source)), + patch("custom_components.pyscript.trigger.dt_now", return_value=now), + patch("homeassistant.config.load_yaml_config_file", return_value={}), + patch("custom_components.pyscript.open", mock_open(read_data=source)), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch( + "custom_components.pyscript.install_requirements", + return_value=None, + ), ): assert await async_setup_component(hass, "pyscript", {DOMAIN: {}}) diff --git a/tests/test_function.py b/tests/test_function.py index 25638fe..419c02c 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -70,12 +70,11 @@ def test_install_ast_funcs(ast_functions): # pylint: disable=redefined-outer-na ids=lambda x: x if not isinstance(x, set) else f"set({len(x)})", ) @pytest.mark.asyncio -async def test_func_completions( - ast_functions, functions, root, expected -): # pylint: disable=redefined-outer-name +async def test_func_completions(ast_functions, functions, root, expected): # pylint: disable=redefined-outer-name """Test function name completion.""" - with patch.object(Function, "ast_functions", ast_functions), patch.object( - Function, "functions", functions + with ( + patch.object(Function, "ast_functions", ast_functions), + patch.object(Function, "functions", functions), ): words = await Function.func_completions(root) assert words == expected @@ -95,8 +94,9 @@ async def test_func_completions( @pytest.mark.asyncio async def test_service_completions(root, expected, hass, services): # pylint: disable=redefined-outer-name """Test service name completion.""" - with patch.object(ServiceRegistry, "async_services", return_value=services), patch.object( - Function, "hass", hass + with ( + patch.object(ServiceRegistry, "async_services", return_value=services), + patch.object(Function, "hass", hass), ): words = await Function.service_completions(root) assert words == expected @@ -129,26 +129,22 @@ def glob_side_effect(path, recursive=None, root_dir=None, dir_fd=None, include_h if not config: config = {DOMAIN: {CONF_ALLOW_ALL_IMPORTS: True}} - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob" - ) as mock_glob, patch("custom_components.pyscript.global_ctx.open", mock_open), patch( - "custom_components.pyscript.trigger.dt_now", return_value=now - ), patch( - "custom_components.pyscript.open", mock_open - ), patch( - "homeassistant.config.load_yaml_config_file", return_value=config - ), patch( - "custom_components.pyscript.install_requirements", - return_value=None, - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.os.path.isfile" - ) as mock_isfile: + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob") as mock_glob, + patch("custom_components.pyscript.global_ctx.open", mock_open), + patch("custom_components.pyscript.trigger.dt_now", return_value=now), + patch("custom_components.pyscript.open", mock_open), + patch("homeassistant.config.load_yaml_config_file", return_value=config), + patch( + "custom_components.pyscript.install_requirements", + return_value=None, + ), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.os.path.isfile") as mock_isfile, + ): mock_isfile.side_effect = isfile_side_effect mock_glob.side_effect = glob_side_effect assert await async_setup_component(hass, "pyscript", config) @@ -1246,12 +1242,14 @@ def service_call_exception(): @pytest.mark.asyncio async def test_service_call_params(hass): """Test that hass params get set properly on service calls.""" - with patch.object(ServiceRegistry, "async_call") as call, patch.object( - Function, "service_has_service", return_value=True - ), patch.object( - ServiceRegistry, - "supports_response", - return_value="none", + with ( + patch.object(ServiceRegistry, "async_call") as call, + patch.object(Function, "service_has_service", return_value=True), + patch.object( + ServiceRegistry, + "supports_response", + return_value="none", + ), ): Function.init(hass) await Function.service_call( diff --git a/tests/test_init.py b/tests/test_init.py index 0e7e282..86bd144 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -28,23 +28,20 @@ async def setup_script(hass, notify_q, now, source, script_name="/hello.py"): Function.hass = None - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob", return_value=scripts - ), patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source)), patch( - "custom_components.pyscript.trigger.dt_now", return_value=now - ), patch( - "custom_components.pyscript.open", mock_open(read_data=source) - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={} - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.install_requirements", - return_value=None, + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob", return_value=scripts), + patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source)), + patch("custom_components.pyscript.trigger.dt_now", return_value=now), + patch("custom_components.pyscript.open", mock_open(read_data=source)), + patch("homeassistant.config.load_yaml_config_file", return_value={}), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch( + "custom_components.pyscript.install_requirements", + return_value=None, + ), ): assert await async_setup_component(hass, "pyscript", {DOMAIN: {}}) @@ -74,10 +71,11 @@ async def wait_until_done(notify_q): @pytest.mark.asyncio async def test_setup_makedirs_on_no_dir(hass, caplog): """Test setup calls os.makedirs when no dir found.""" - with patch("custom_components.pyscript.os.path.isdir", return_value=False), patch( - "custom_components.pyscript.os.makedirs" - ), patch("custom_components.pyscript.watchdog_start", return_value=None) as makedirs_call, patch( - "homeassistant.config.load_yaml_config_file", return_value={} + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=False), + patch("custom_components.pyscript.os.makedirs"), + patch("custom_components.pyscript.watchdog_start", return_value=None) as makedirs_call, + patch("homeassistant.config.load_yaml_config_file", return_value={}), ): res = await async_setup_component(hass, "pyscript", {DOMAIN: {}}) @@ -429,21 +427,19 @@ def func5(var_name=None, value=None): "/hello.py", ] - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob", return_value=scripts - ), patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=next_source)), patch( - "custom_components.pyscript.open", mock_open(read_data=next_source) - ), patch( - "custom_components.pyscript.trigger.dt_now", return_value=now - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={} - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.install_requirements", - return_value=None, + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob", return_value=scripts), + patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=next_source)), + patch("custom_components.pyscript.open", mock_open(read_data=next_source)), + patch("custom_components.pyscript.trigger.dt_now", return_value=now), + patch("homeassistant.config.load_yaml_config_file", return_value={}), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch( + "custom_components.pyscript.install_requirements", + return_value=None, + ), ): reload_param = {} if i % 2 == 1: diff --git a/tests/test_jupyter.py b/tests/test_jupyter.py index bae42ff..d6284b0 100644 --- a/tests/test_jupyter.py +++ b/tests/test_jupyter.py @@ -131,26 +131,22 @@ def glob_side_effect(path, recursive=None, root_dir=None, dir_fd=None, include_h result.append(this_path) return result - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob" - ) as mock_glob, patch("custom_components.pyscript.global_ctx.open", mock_open), patch( - "custom_components.pyscript.trigger.dt_now", return_value=now - ), patch( - "custom_components.pyscript.open", mock_open - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={} - ), patch( - "custom_components.pyscript.install_requirements", - return_value=None, - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.os.path.isfile" - ) as mock_isfile: + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob") as mock_glob, + patch("custom_components.pyscript.global_ctx.open", mock_open), + patch("custom_components.pyscript.trigger.dt_now", return_value=now), + patch("custom_components.pyscript.open", mock_open), + patch("homeassistant.config.load_yaml_config_file", return_value={}), + patch( + "custom_components.pyscript.install_requirements", + return_value=None, + ), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.os.path.isfile") as mock_isfile, + ): mock_isfile.side_effect = isfile_side_effect mock_glob.side_effect = glob_side_effect assert await async_setup_component(hass, "pyscript", {DOMAIN: {}}) diff --git a/tests/test_reload.py b/tests/test_reload.py index 2a1b3b3..67f5466 100644 --- a/tests/test_reload.py +++ b/tests/test_reload.py @@ -133,23 +133,18 @@ def glob_side_effect(path, recursive=None, root_dir=None, dir_fd=None, include_h return result conf = {"apps": {"world": {}, "world2": {"var1": 100}}} - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob" - ) as mock_glob, patch("custom_components.pyscript.global_ctx.open", mock_open), patch( - "custom_components.pyscript.open", mock_open - ), patch( - "homeassistant.util.yaml.loader.open", mock_open - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={"pyscript": conf} - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.os.path.isfile" - ) as mock_isfile: + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob") as mock_glob, + patch("custom_components.pyscript.global_ctx.open", mock_open), + patch("custom_components.pyscript.open", mock_open), + patch("homeassistant.util.yaml.loader.open", mock_open), + patch("homeassistant.config.load_yaml_config_file", return_value={"pyscript": conf}), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.os.path.isfile") as mock_isfile, + ): mock_isfile.side_effect = isfile_side_effect mock_glob.side_effect = glob_side_effect assert await async_setup_component(hass, "pyscript", {DOMAIN: conf}) @@ -178,9 +173,7 @@ async def state_changed(event): # # add a new script file # - file_contents[ - f"{conf_dir}/hello2.py" - ] = """ + file_contents[f"{conf_dir}/hello2.py"] = """ log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()};") @service @@ -223,9 +216,7 @@ def func20(): # # change a module file and confirm the parent script is reloaded too # - file_contents[ - f"{conf_dir}/modules/xyz2/other.py" - ] = """ + file_contents[f"{conf_dir}/modules/xyz2/other.py"] = """ log.info(f"modules/xyz2/other global_ctx={pyscript.get_global_ctx()};") xyz = 456 @@ -248,9 +239,7 @@ def func20(): # # change a module inside an app # - file_contents[ - f"{conf_dir}/apps/world2/other.py" - ] = """ + file_contents[f"{conf_dir}/apps/world2/other.py"] = """ other_abc = 654 log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()}") diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 67e57f8..2ca4c33 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -33,11 +33,12 @@ def bypass_package_install_fixture(): @pytest.mark.asyncio async def test_install_requirements(hass, caplog): """Test install_requirements function.""" - with patch( - "custom_components.pyscript.requirements.process_all_requirements" - ) as process_requirements, patch( - "custom_components.pyscript.requirements.async_process_requirements" - ) as ha_install_requirements: + with ( + patch("custom_components.pyscript.requirements.process_all_requirements") as process_requirements, + patch( + "custom_components.pyscript.requirements.async_process_requirements" + ) as ha_install_requirements, + ): entry = MockConfigEntry(domain=DOMAIN, data={CONF_ALLOW_ALL_IMPORTS: True}) entry.add_to_hass(hass) @@ -161,11 +162,12 @@ async def test_install_requirements(hass, caplog): @pytest.mark.asyncio async def test_install_unpinned_requirements(hass, caplog): """Test install_requirements function with unpinned versions.""" - with patch( - "custom_components.pyscript.requirements.process_all_requirements" - ) as process_requirements, patch( - "custom_components.pyscript.requirements.async_process_requirements" - ) as ha_install_requirements: + with ( + patch("custom_components.pyscript.requirements.process_all_requirements") as process_requirements, + patch( + "custom_components.pyscript.requirements.async_process_requirements" + ) as ha_install_requirements, + ): entry = MockConfigEntry(domain=DOMAIN, data={CONF_ALLOW_ALL_IMPORTS: True}) entry.add_to_hass(hass) @@ -272,11 +274,12 @@ async def test_install_unpinned_requirements(hass, caplog): @pytest.mark.asyncio async def test_install_requirements_not_allowed(hass): """Test that install requirements will not work because 'allow_all_imports' is False.""" - with patch( - "custom_components.pyscript.requirements.process_all_requirements" - ) as process_requirements, patch( - "custom_components.pyscript.requirements.async_process_requirements" - ) as ha_install_requirements: + with ( + patch("custom_components.pyscript.requirements.process_all_requirements") as process_requirements, + patch( + "custom_components.pyscript.requirements.async_process_requirements" + ) as ha_install_requirements, + ): entry = MockConfigEntry(domain=DOMAIN, data={CONF_ALLOW_ALL_IMPORTS: False}) entry.add_to_hass(hass) diff --git a/tests/test_state.py b/tests/test_state.py index f6be05a..488104b 100644 --- a/tests/test_state.py +++ b/tests/test_state.py @@ -15,16 +15,21 @@ @pytest.mark.asyncio async def test_service_call(hass): """Test calling a service using the entity_id as a property.""" - with patch( - "custom_components.pyscript.state.async_get_all_descriptions", - return_value={ - "test": { - "test": {"description": None, "fields": {"entity_id": "blah", "other_service_data": "blah"}} - } - }, - ), patch.object(StateMachine, "get", return_value=HassState("test.entity", "True")), patch.object( - ServiceRegistry, "async_call" - ) as call: + with ( + patch( + "custom_components.pyscript.state.async_get_all_descriptions", + return_value={ + "test": { + "test": { + "description": None, + "fields": {"entity_id": "blah", "other_service_data": "blah"}, + } + } + }, + ), + patch.object(StateMachine, "get", return_value=HassState("test.entity", "True")), + patch.object(ServiceRegistry, "async_call") as call, + ): State.init(hass) Function.init(hass) await State.get_service_params() diff --git a/tests/test_stubs.py b/tests/test_stubs.py index 8b41040..d1b3650 100644 --- a/tests/test_stubs.py +++ b/tests/test_stubs.py @@ -11,7 +11,6 @@ from custom_components.pyscript.const import DOMAIN, FOLDER, SERVICE_GENERATE_STUBS from homeassistant.core import HomeAssistant - from tests.test_init import setup_script diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 92845fc..20e95cb 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -137,21 +137,17 @@ def glob_side_effect(path, recursive=None, root_dir=None, dir_fd=None, include_h return result conf = {"apps": {"world": {}}} - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob" - ) as mock_glob, patch("custom_components.pyscript.global_ctx.open", mock_open), patch( - "custom_components.pyscript.open", mock_open - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={"pyscript": conf} - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.os.path.isfile" - ) as mock_isfile: + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob") as mock_glob, + patch("custom_components.pyscript.global_ctx.open", mock_open), + patch("custom_components.pyscript.open", mock_open), + patch("homeassistant.config.load_yaml_config_file", return_value={"pyscript": conf}), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.os.path.isfile") as mock_isfile, + ): mock_isfile.side_effect = isfile_side_effect mock_glob.side_effect = glob_side_effect assert await async_setup_component(hass, "pyscript", {DOMAIN: conf}) diff --git a/tests/test_unique.py b/tests/test_unique.py index b698119..b0deb18 100644 --- a/tests/test_unique.py +++ b/tests/test_unique.py @@ -22,23 +22,20 @@ async def setup_script(hass, notify_q, now, source): Function.hass = None - with patch("custom_components.pyscript.os.path.isdir", return_value=True), patch( - "custom_components.pyscript.glob.iglob", return_value=scripts - ), patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source)), patch( - "custom_components.pyscript.open", mock_open(read_data=source) - ), patch( - "custom_components.pyscript.trigger.dt_now", return_value=now - ), patch( - "homeassistant.config.load_yaml_config_file", return_value={} - ), patch( - "custom_components.pyscript.watchdog_start", return_value=None - ), patch( - "custom_components.pyscript.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000 - ), patch( - "custom_components.pyscript.install_requirements", - return_value=None, + with ( + patch("custom_components.pyscript.os.path.isdir", return_value=True), + patch("custom_components.pyscript.glob.iglob", return_value=scripts), + patch("custom_components.pyscript.global_ctx.open", mock_open(read_data=source)), + patch("custom_components.pyscript.open", mock_open(read_data=source)), + patch("custom_components.pyscript.trigger.dt_now", return_value=now), + patch("homeassistant.config.load_yaml_config_file", return_value={}), + patch("custom_components.pyscript.watchdog_start", return_value=None), + patch("custom_components.pyscript.os.path.getmtime", return_value=1000), + patch("custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), + patch( + "custom_components.pyscript.install_requirements", + return_value=None, + ), ): assert await async_setup_component(hass, "pyscript", {DOMAIN: {}}) diff --git a/tests/test_unit_trigger.py b/tests/test_unit_trigger.py index c2860c6..cde6377 100644 --- a/tests/test_unit_trigger.py +++ b/tests/test_unit_trigger.py @@ -84,8 +84,9 @@ async def test_parse_date_time(hass, caplog): # now = dt(2019, 9, 1, 13, 0, 0, 0) - with patch("homeassistant.helpers.condition.dt_util.utcnow", return_value=now), patch( - "homeassistant.util.dt.utcnow", return_value=now + with ( + patch("homeassistant.helpers.condition.dt_util.utcnow", return_value=now), + patch("homeassistant.util.dt.utcnow", return_value=now), ): for test_data in parseDateTimeTests: spec, date_offset, expect = test_data @@ -123,8 +124,9 @@ async def test_parse_date_time_day_names(hass, caplog): # This set of tests assumes it's currently 13:00 on 2019/9/3 # now = dt(2019, 9, 3, 13, 0, 0, 0) - with patch("homeassistant.helpers.condition.dt_util.utcnow", return_value=now), patch( - "homeassistant.util.dt.utcnow", return_value=now + with ( + patch("homeassistant.helpers.condition.dt_util.utcnow", return_value=now), + patch("homeassistant.util.dt.utcnow", return_value=now), ): for test_data in parseDateTimeTestsDayNames: spec, date_offset, expect = test_data