From cb65b0ee66370d0859530a542bde85ff14272161 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Sun, 14 Jun 2026 12:32:07 +0200 Subject: [PATCH 01/12] include basic pyrefly config --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index e8d882755..d005fb578 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,10 @@ include-package-data = true write_to = "conda_smithy/_version.py" write_to_template = "__version__ = '{version}'" +[tool.pyrefly] +project-includes = ["**/*.py"] +search-path = [".", "tests"] + [tool.black] # matches black's default value line-length = 88 From 17c524a3eae014df70995a42de59fa972fb24e66 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Sun, 14 Jun 2026 13:20:50 +0200 Subject: [PATCH 02/12] first fixes for pyrefly --- bootstrap-obvious-ci-and-miniconda.py | 6 +----- conda_smithy/anaconda_token_rotation.py | 2 ++ conda_smithy/ci_register.py | 4 ++-- conda_smithy/configure_feedstock.py | 4 +++- conda_smithy/deprecations.py | 3 ++- conda_smithy/feedstock_io.py | 11 ++++++----- conda_smithy/github.py | 2 +- conda_smithy/linter/hints.py | 1 + conda_smithy/linter/lints.py | 1 + conda_smithy/plugin.py | 4 +++- conda_smithy/validate_schema.py | 6 +++--- tests/test_configure_feedstock.py | 14 ++++++-------- tests/test_lint_recipe.py | 3 ++- 13 files changed, 33 insertions(+), 28 deletions(-) diff --git a/bootstrap-obvious-ci-and-miniconda.py b/bootstrap-obvious-ci-and-miniconda.py index 8b380692c..b42d9b174 100644 --- a/bootstrap-obvious-ci-and-miniconda.py +++ b/bootstrap-obvious-ci-and-miniconda.py @@ -11,11 +11,7 @@ import os import platform import subprocess - -try: - from urllib.request import urlretrieve -except ImportError: - from urllib import urlretrieve +from urllib.request import urlretrieve MINICONDA_URL_TEMPLATE = ( "https://repo.continuum.io/miniconda/Miniconda{major_py_version}-" diff --git a/conda_smithy/anaconda_token_rotation.py b/conda_smithy/anaconda_token_rotation.py index b10772061..bcd6091df 100644 --- a/conda_smithy/anaconda_token_rotation.py +++ b/conda_smithy/anaconda_token_rotation.py @@ -62,6 +62,7 @@ def rotate_anaconda_token( anaconda_token = _get_anaconda_token() + gh = None if github_actions: gh = Github(gh_token()) @@ -167,6 +168,7 @@ def rotate_anaconda_token( raise RuntimeError(err_msg) if github_actions: + assert gh is not None try: rotate_token_in_github_actions( user, project, anaconda_token, token_name, gh diff --git a/conda_smithy/ci_register.py b/conda_smithy/ci_register.py index 4e5262d47..ddd81480b 100755 --- a/conda_smithy/ci_register.py +++ b/conda_smithy/ci_register.py @@ -84,11 +84,11 @@ class LiveServerSession(requests.Session): """Utility class to avoid typing out urls all the time""" - def __init__(self, prefix_url=None, *args, **kwargs): + def __init__(self, prefix_url: str = "", *args, **kwargs): super().__init__(*args, **kwargs) self.prefix_url = prefix_url - def request(self, method, url, *args, **kwargs): + def request(self, method, url: str, *args, **kwargs): from urllib.parse import urljoin url = urljoin(self.prefix_url, url) diff --git a/conda_smithy/configure_feedstock.py b/conda_smithy/configure_feedstock.py index ae9c0f538..75d7861de 100644 --- a/conda_smithy/configure_feedstock.py +++ b/conda_smithy/configure_feedstock.py @@ -68,6 +68,8 @@ validate_json_schema, ) +JSONDecodeError = json.JSONDecodeError + conda_forge_content = os.path.abspath(os.path.dirname(__file__)) logger = logging.getLogger(__name__) @@ -2358,7 +2360,7 @@ def render_readme(jinja_env, forge_config, forge_dir, render_info=None): "Azure build_id can't be retrieved using the Azure token. Exception: %s", err, ) - except json.decoder.JSONDecodeError: + except JSONDecodeError: azure_build_id_from_token(forge_config) logger.debug("README") diff --git a/conda_smithy/deprecations.py b/conda_smithy/deprecations.py index b3449f4ff..0c410aa9d 100644 --- a/conda_smithy/deprecations.py +++ b/conda_smithy/deprecations.py @@ -442,7 +442,8 @@ def _generate_message( raise ValueError( "'deprecate_in' version needs at least three components" ) - next_version = datetime(*deprecate_in_tuple[:3]) + remove_in + year, month, day = deprecate_in_tuple[:3] + next_version = datetime(year, month, day) + remove_in remove_in = f"{next_version.year}.{next_version.month}.{next_version.day}" if self._version_less_than(deprecate_in): category = PendingDeprecationWarning diff --git a/conda_smithy/feedstock_io.py b/conda_smithy/feedstock_io.py index 9299b2524..8cc20ec34 100644 --- a/conda_smithy/feedstock_io.py +++ b/conda_smithy/feedstock_io.py @@ -9,7 +9,10 @@ def get_repo(path, search_parent_directories=True): repo = None try: import pygit2 + except ImportError: + return None + try: if search_parent_directories: path = pygit2.discover_repository(path) if path is not None: @@ -19,8 +22,6 @@ def get_repo(path, search_parent_directories=True): no_search = pygit2.GIT_REPOSITORY_OPEN_NO_SEARCH repo = pygit2.Repository(path, no_search) - except ImportError: - pass except pygit2.GitError: pass @@ -28,10 +29,10 @@ def get_repo(path, search_parent_directories=True): def get_repo_root(path): - try: - return get_repo(path).workdir.rstrip(os.path.sep) - except AttributeError: + repo = get_repo(path) + if repo is None: return None + return repo.workdir.rstrip(os.path.sep) def set_exe_file(filename, set_exe=True): diff --git a/conda_smithy/github.py b/conda_smithy/github.py index 7da676cab..b82836e33 100644 --- a/conda_smithy/github.py +++ b/conda_smithy/github.py @@ -598,7 +598,7 @@ def configure_github_team( def configure_github_app( org: str, repo: str, - app_slug_or_installation_id: str | int = None, + app_slug_or_installation_id: str | int, remove: bool = False, ) -> None: """ diff --git a/conda_smithy/linter/hints.py b/conda_smithy/linter/hints.py index 8572b405c..db862f6b8 100644 --- a/conda_smithy/linter/hints.py +++ b/conda_smithy/linter/hints.py @@ -71,6 +71,7 @@ def hint_suggest_noarch( else: with open(recipe_fname, encoding="utf-8") as fh: in_runreqs = False + runreqs_spacing = "" no_arch_possible = True for line in fh: line_s = line.strip() diff --git a/conda_smithy/linter/lints.py b/conda_smithy/linter/lints.py index a7e3597a5..c01dd0dba 100644 --- a/conda_smithy/linter/lints.py +++ b/conda_smithy/linter/lints.py @@ -324,6 +324,7 @@ def lint_noarch_and_runtime_dependencies( noarch_platforms = len(forge_yaml.get("noarch_platforms", [])) > 1 with open(meta_fname, encoding="utf-8") as fh: in_runreqs = False + runreqs_spacing = "" for line_number, line in enumerate(fh, 1): line_s = line.strip() if line_s == "host:" or line_s == "run:": diff --git a/conda_smithy/plugin.py b/conda_smithy/plugin.py index 886bb9b60..fdf678a23 100644 --- a/conda_smithy/plugin.py +++ b/conda_smithy/plugin.py @@ -6,11 +6,13 @@ from __future__ import annotations +from argparse import Namespace + from conda.plugins import hookimpl from conda.plugins.types import CondaSubcommand -def _execute(args: tuple[str, ...]) -> int | None: +def _execute(args: Namespace | tuple[str, ...]) -> int | None: """Dispatch plugin arguments to the smithy CLI. Lazy import to avoid import-time side effects when not using conda-smithy. diff --git a/conda_smithy/validate_schema.py b/conda_smithy/validate_schema.py index c68edc677..03905b21c 100644 --- a/conda_smithy/validate_schema.py +++ b/conda_smithy/validate_schema.py @@ -19,7 +19,7 @@ class DeprecatedValidator: def __init__(self): - self.hints = [] + self.hints: list[str] = [] def __call__(self, validator, value, instance, schema): if value and instance is not None: @@ -38,8 +38,8 @@ def get_validator_class(deprecated_validator): def validate_json_schema( - config, schema_file: str = None -) -> tuple[list[ValidationError], list[ValidationError]]: + config, schema_file: str | Path | None = None, +) -> tuple[list[ValidationError], list[str]]: # Validate the merged configuration against a JSON schema if not schema_file: schema_file = CONDA_FORGE_YAML_SCHEMA_FILE diff --git a/tests/test_configure_feedstock.py b/tests/test_configure_feedstock.py index 9a4aca634..bad8331d7 100644 --- a/tests/test_configure_feedstock.py +++ b/tests/test_configure_feedstock.py @@ -1040,9 +1040,8 @@ def load_forge_config(forge_yml): def test_cos7_env_render(py_recipe, jinja_env): forge_config = copy.deepcopy(py_recipe.config) forge_config["os_version"] = {"linux_64": "cos7"} - has_env = "DEFAULT_LINUX_VERSION" in os.environ - if has_env: - old_val = os.environ["DEFAULT_LINUX_VERSION"] + old_val = os.environ.get("DEFAULT_LINUX_VERSION") + if old_val is not None: del os.environ["DEFAULT_LINUX_VERSION"] try: @@ -1064,7 +1063,7 @@ def test_cos7_env_render(py_recipe, jinja_env): assert len(os.listdir(matrix_dir)) == 6 finally: - if has_env: + if old_val is not None: os.environ["DEFAULT_LINUX_VERSION"] = old_val else: if "DEFAULT_LINUX_VERSION" in os.environ: @@ -1073,9 +1072,8 @@ def test_cos7_env_render(py_recipe, jinja_env): def test_cuda_enabled_render(cuda_enabled_recipe, jinja_env): forge_config = copy.deepcopy(cuda_enabled_recipe.config) - has_env = "CF_CUDA_ENABLED" in os.environ - if has_env: - old_val = os.environ["CF_CUDA_ENABLED"] + old_val = os.environ.get("CF_CUDA_ENABLED") + if old_val is not None: del os.environ["CF_CUDA_ENABLED"] try: @@ -1098,7 +1096,7 @@ def test_cuda_enabled_render(cuda_enabled_recipe, jinja_env): assert len(os.listdir(matrix_dir)) == 6 finally: - if has_env: + if old_val is not None: os.environ["CF_CUDA_ENABLED"] = old_val else: if "CF_CUDA_ENABLED" in os.environ: diff --git a/tests/test_lint_recipe.py b/tests/test_lint_recipe.py index bef2df82c..2fe9b39d2 100644 --- a/tests/test_lint_recipe.py +++ b/tests/test_lint_recipe.py @@ -9,6 +9,7 @@ import textwrap import unittest from collections import OrderedDict +from collections.abc import Iterator from contextlib import contextmanager from itertools import count from pathlib import Path @@ -29,7 +30,7 @@ @contextmanager -def get_recipe_in_dir(recipe_name: str) -> Path: +def get_recipe_in_dir(recipe_name: str) -> Iterator[Path]: base_dir = Path(__file__).parent recipe_path = base_dir / "recipes" / recipe_name assert recipe_path.exists(), f"Recipe {recipe_name} does not exist" From 623c5789c60adde154c6a8eff416b3a6cdd47dc7 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Sun, 14 Jun 2026 13:23:34 +0200 Subject: [PATCH 03/12] black --- conda_smithy/validate_schema.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conda_smithy/validate_schema.py b/conda_smithy/validate_schema.py index 03905b21c..d38e54ed5 100644 --- a/conda_smithy/validate_schema.py +++ b/conda_smithy/validate_schema.py @@ -38,7 +38,8 @@ def get_validator_class(deprecated_validator): def validate_json_schema( - config, schema_file: str | Path | None = None, + config, + schema_file: str | Path | None = None, ) -> tuple[list[ValidationError], list[str]]: # Validate the merged configuration against a JSON schema if not schema_file: From d9c25667e907faac9279ede70e01e954c13dc814 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Sun, 14 Jun 2026 13:28:54 +0200 Subject: [PATCH 04/12] news --- news/2593.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 news/2593.rst diff --git a/news/2593.rst b/news/2593.rst new file mode 100644 index 000000000..bb07b3912 --- /dev/null +++ b/news/2593.rst @@ -0,0 +1,3 @@ +**Fixed:** + +* Fixed several small typing issues reported by Pyrefly. From 1434bef6093e875c4e9f3a3cd77814753e8618d1 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Sun, 14 Jun 2026 13:38:53 +0200 Subject: [PATCH 05/12] Update conda_smithy/feedstock_io.py Co-authored-by: h-vetinari --- conda_smithy/feedstock_io.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conda_smithy/feedstock_io.py b/conda_smithy/feedstock_io.py index 8cc20ec34..b9fb2d638 100644 --- a/conda_smithy/feedstock_io.py +++ b/conda_smithy/feedstock_io.py @@ -29,8 +29,7 @@ def get_repo(path, search_parent_directories=True): def get_repo_root(path): - repo = get_repo(path) - if repo is None: + if (repo := get_repo(path)) is None: return None return repo.workdir.rstrip(os.path.sep) From 34dc0ae197f7c26d0711443483f89d627e8fc293 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Thu, 9 Jul 2026 11:28:00 +0200 Subject: [PATCH 06/12] apply suggestion --- conda_smithy/anaconda_token_rotation.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/conda_smithy/anaconda_token_rotation.py b/conda_smithy/anaconda_token_rotation.py index bcd6091df..4d0dc433f 100644 --- a/conda_smithy/anaconda_token_rotation.py +++ b/conda_smithy/anaconda_token_rotation.py @@ -62,10 +62,6 @@ def rotate_anaconda_token( anaconda_token = _get_anaconda_token() - gh = None - if github_actions: - gh = Github(gh_token()) - # capture stdout, stderr and suppress all exceptions so we don't # spill tokens failed = False @@ -145,8 +141,7 @@ def rotate_anaconda_token( raise e else: err_msg = ( - f"Failed to rotate token for {user}/{project}" - " on azure!" + f"Failed to rotate token for {user}/{project} on azure!" ) failed = True raise RuntimeError(err_msg) @@ -168,7 +163,7 @@ def rotate_anaconda_token( raise RuntimeError(err_msg) if github_actions: - assert gh is not None + gh = Github(gh_token()) try: rotate_token_in_github_actions( user, project, anaconda_token, token_name, gh From d0274205d3c4a204df05ad45a30c706676cf3475 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Wed, 15 Jul 2026 16:43:33 +0200 Subject: [PATCH 07/12] improve runreqs_spacing error --- conda_smithy/linter/hints.py | 8 +++----- conda_smithy/linter/lints.py | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/conda_smithy/linter/hints.py b/conda_smithy/linter/hints.py index db862f6b8..92a26f035 100644 --- a/conda_smithy/linter/hints.py +++ b/conda_smithy/linter/hints.py @@ -70,21 +70,19 @@ def hint_suggest_noarch( ) else: with open(recipe_fname, encoding="utf-8") as fh: - in_runreqs = False - runreqs_spacing = "" + runreqs_spacing = None no_arch_possible = True for line in fh: line_s = line.strip() if line_s == "host:" or line_s == "run:": - in_runreqs = True runreqs_spacing = line[: -len(line.lstrip())] continue if line_s.startswith("skip:") and is_selector_line(line): no_arch_possible = False break - if in_runreqs: + if runreqs_spacing is not None: if runreqs_spacing == line[: -len(line.lstrip())]: - in_runreqs = False + runreqs_spacing = None continue if is_selector_line(line): no_arch_possible = False diff --git a/conda_smithy/linter/lints.py b/conda_smithy/linter/lints.py index 79a13892b..b9e816b5a 100644 --- a/conda_smithy/linter/lints.py +++ b/conda_smithy/linter/lints.py @@ -327,12 +327,10 @@ def lint_noarch_and_runtime_dependencies( return noarch_platforms = len(forge_yaml.get("noarch_platforms", [])) > 1 with open(meta_fname, encoding="utf-8") as fh: - in_runreqs = False - runreqs_spacing = "" + runreqs_spacing = None for line_number, line in enumerate(fh, 1): line_s = line.strip() if line_s == "host:" or line_s == "run:": - in_runreqs = True runreqs_spacing = line[: -len(line.lstrip())] continue if line_s.startswith("skip:") and is_selector_line(line): @@ -345,9 +343,9 @@ def lint_noarch_and_runtime_dependencies( ).as_string() ) break - if in_runreqs: + if runreqs_spacing is not None: if runreqs_spacing == line[: -len(line.lstrip())]: - in_runreqs = False + runreqs_spacing = None continue if is_selector_line( line, From 6779e3ef06218f35f5560efb7ca032085d031a15 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Wed, 15 Jul 2026 17:41:37 +0200 Subject: [PATCH 08/12] add minimal contributing.md --- CONTRIBUTING.md | 34 ++++++++++++++++++++++++++++++++++ environment.yml | 1 + 2 files changed, 35 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..bf20d52c6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Developing conda-smithy + +## Set up Development Environment + +To install conda-smithy from source: + +* Install `conda` +* Fork and clone this repository: `git clone https://github.com/YOUR-USERNAME/conda-smithy.git`. Change to it: `cd conda-smithy`. +* Create a new conda environment with all requirements based on [environment.yml](environment.yml): `conda env create`. +* Activate the environment: `conda activate conda-smithy`. +* Install conda-smithy: `pip install --no-deps --editable .` + +To run all tests: + +```sh +$ pytest +``` + +To run all code checks: + +```sh +# staged changes +$ pre-commit run +# all files +$ pre-commit run --all-files +``` + +To run pyrefly code checks: + +```sh +$ pyrefly check --python-interpreter-path $(which python) --output-format min-text --count-errors=1 --search-path . +``` + +If you encounter pyrefly issues that you don't agree with, feel free to add a `# pyrefly: ignore[]` comment to that line. diff --git a/environment.yml b/environment.yml index 6a7535901..46ff0a54c 100644 --- a/environment.yml +++ b/environment.yml @@ -13,6 +13,7 @@ dependencies: - mock - pytest - pytest-cov + - pyrefly # # Part of some optional lint tests - conda-recipe-manager>=0.9 - conda-souschef From 82c1e05fdd0d51c8c6e1cb7ce900ec3d2b74d2d1 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Wed, 15 Jul 2026 17:44:27 +0200 Subject: [PATCH 09/12] Update CONTRIBUTING.md Co-authored-by: Lucas Colley --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bf20d52c6..02392c593 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,3 +32,4 @@ $ pyrefly check --python-interpreter-path $(which python) --output-format min-te ``` If you encounter pyrefly issues that you don't agree with, feel free to add a `# pyrefly: ignore[]` comment to that line. +An ignored `pyrefly` error is insufficient reason to block the merge of a pull request. From d2f8703a83bd9ad6147b4eeb195c2e2c221104fb Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Wed, 15 Jul 2026 18:05:17 +0200 Subject: [PATCH 10/12] Update 2593.rst --- news/2593.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/2593.rst b/news/2593.rst index bb07b3912..ac2dc35a9 100644 --- a/news/2593.rst +++ b/news/2593.rst @@ -1,3 +1,3 @@ **Fixed:** -* Fixed several small typing issues reported by Pyrefly. +* Fixed several small typing issues reported by Pyrefly. (#2593) From 3482ae1011264055ec69e97aefecef2e5e6b49fe Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Wed, 15 Jul 2026 18:31:09 +0200 Subject: [PATCH 11/12] Update CONTRIBUTING.md Co-authored-by: Isuru Fernando --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02392c593..5f8a2d4a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ To install conda-smithy from source: * Install `conda` * Fork and clone this repository: `git clone https://github.com/YOUR-USERNAME/conda-smithy.git`. Change to it: `cd conda-smithy`. -* Create a new conda environment with all requirements based on [environment.yml](environment.yml): `conda env create`. +* Create a new conda environment with all requirements based on [environment.yml](environment.yml): `conda env create -f environment.yml`. * Activate the environment: `conda activate conda-smithy`. * Install conda-smithy: `pip install --no-deps --editable .` From 26d5e3dbb8bdd25d1e34f2ce8136f4e5bef5db95 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Fri, 24 Jul 2026 16:38:48 +0200 Subject: [PATCH 12/12] Update pyproject.toml Co-authored-by: jaimergp --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d005fb578..880500615 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ write_to = "conda_smithy/_version.py" write_to_template = "__version__ = '{version}'" [tool.pyrefly] -project-includes = ["**/*.py"] +project-includes = ["conda_smithy/**/*.py", "tests/**/*.py"] search-path = [".", "tests"] [tool.black]