diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 852f382c..6a5de45c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -53,12 +53,12 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Run tox - uses: lsst-sqre/run-tox@v1 + - name: Run nox + uses: lsst-sqre/run-nox@v1 with: + cache-dependency: "*/pyproject.toml" + nox-sessions: "typing test" python-version: ${{ matrix.python }} - tox-envs: "py,typing" - tox-plugins: "tox-uv" docs: @@ -73,12 +73,12 @@ jobs: - name: Install Graphviz run: sudo apt-get install graphviz - - name: Run tox - uses: lsst-sqre/run-tox@v1 + - name: Run nox + uses: lsst-sqre/run-nox@v1 with: - python-version: "3.12" - tox-envs: "docs,docs-linkcheck" - tox-plugins: tox-uv + cache-dependency: "*/pyproject.toml" + nox-sessions: "docs docs-linkcheck" + python-version: ${{ env.PYTHON_VERSION }} # Only attempt documentation uploads for tagged releases and pull # requests from ticket branches in the same repository. This avoids @@ -113,6 +113,7 @@ jobs: with: python-version: ${{ env.PYTHON_VERSION }} upload: false + working-directory: "safir" pypi: @@ -135,3 +136,4 @@ jobs: - uses: lsst-sqre/build-and-publish-to-pypi@v2 with: python-version: ${{ env.PYTHON_VERSION }} + working-directory: "safir" diff --git a/.github/workflows/periodic-ci.yaml b/.github/workflows/periodic-ci.yaml index b787b6ab..c9d2da65 100644 --- a/.github/workflows/periodic-ci.yaml +++ b/.github/workflows/periodic-ci.yaml @@ -30,12 +30,11 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Run tests in tox - uses: lsst-sqre/run-tox@v1 + - name: Run tests in nox + uses: lsst-sqre/run-nox@v1 with: + nox-sessions: "update-deps lint typing test" python-version: ${{ matrix.python }} - tox-envs: "lint,typing,py" - tox-plugins: "tox-uv" use-cache: false - name: Report status @@ -58,12 +57,11 @@ jobs: - name: Install Graphviz run: sudo apt-get install graphviz - - name: Build docs in tox - uses: lsst-sqre/run-tox@v1 + - name: Build docs in nox + uses: lsst-sqre/run-nox@v1 with: + nox-sessions: "docs docs-linkcheck" python-version: ${{ env.PYTHON_VERSION }} - tox-envs: "docs,docs-linkcheck" - tox-plugins: tox-uv use-cache: false - name: Report status @@ -91,6 +89,7 @@ jobs: with: python-version: ${{ env.PYTHON_VERSION }} upload: false + working-directory: "safir" - name: Report status if: failure() diff --git a/changelog.d/20240719_160937_rra_DM_45281_queue.md b/changelog.d/20240719_160937_rra_DM_45281_queue.md new file mode 100644 index 00000000..89a23393 --- /dev/null +++ b/changelog.d/20240719_160937_rra_DM_45281_queue.md @@ -0,0 +1,3 @@ +### Other changes + +- Safir is now built with [nox](https://nox.thea.codes/en/stable/index.html) instead of [tox](https://tox.wiki/). diff --git a/docs/_rst_epilog.rst b/docs/_rst_epilog.rst index e80423de..58401214 100644 --- a/docs/_rst_epilog.rst +++ b/docs/_rst_epilog.rst @@ -9,6 +9,7 @@ .. _HTTPX: https://www.python-httpx.org/ .. _kubernetes_asyncio: https://github.com/tomplus/kubernetes_asyncio .. _mypy: https://www.mypy-lang.org +.. _nox: https://nox.thea.codes/en/stable/ .. _Phalanx: https://phalanx.lsst.io .. _pre-commit: https://pre-commit.com .. _Pydantic: https://docs.pydantic.dev/latest/ @@ -25,3 +26,4 @@ .. _tox: https://tox.wiki/en/latest/ .. _tox-docker: https://tox-docker.readthedocs.io/en/latest/ .. _Uvicorn: https://www.uvicorn.org/ +.. _virtualenvwrapper: https://virtualenvwrapper.readthedocs.io/en/stable/ diff --git a/docs/dev/development.rst b/docs/dev/development.rst index 5bb38798..59c2c5cb 100644 --- a/docs/dev/development.rst +++ b/docs/dev/development.rst @@ -21,19 +21,31 @@ Safir is developed by the LSST SQuaRE team. Setting up a local development environment ========================================== -To develop Safir, create a virtual environment with your method of choice (like virtualenvwrapper) and then clone or fork, and install: +Development of Safir should be done inside a virtual environment. -.. code-block:: sh +Nublado uses nox_ as its build system, which can manage a virtual environment for you. +Run: + +.. prompt:: bash + + nox -s venv-init + +The resulting virtual environment will be created in :file:`.venv`. +Enable it by running :command:`source .venv/bin/activate`. - git clone https://github.com/lsst-sqre/safir.git - cd safir - make init +Alternately, you can create a virtual environment with any other method of your choice (such as virtualenvwrapper_). +If you use a different virtual environment, run the following command after you have enabled it: -This init step does three things: +.. prompt:: bash -1. Installs Safir in an editable mode with its "dev" extra that includes test and documentation dependencies. -2. Installs pre-commit and tox. -3. Installs the pre-commit hooks. + nox -s init + +Either ``venv-init`` or ``init`` does the following: + +#. Installs build system dependencies in the virtual environment. +#. Installs package dependencies, including test and documentation dependencies. +#. Installs Safir packages in editable mode so that changes made to the Git checkout will be picked up by the virtual environment. +#. Installs pre-commit hooks. You must have Docker installed and configured so that your user can start Docker containers in order to run the test suite. @@ -45,14 +57,8 @@ Pre-commit hooks The pre-commit hooks, which are automatically installed by running the :command:`make init` command on :ref:`set up `, ensure that files are valid and properly formatted. Some pre-commit hooks automatically reformat code: -``seed-isort-config`` - Adds configuration for isort to the :file:`pyproject.toml` file. - -``isort`` - Automatically sorts imports in Python modules. - -``black`` - Automatically formats Python code. +``ruff`` + Lint and reformat Python code and attempt to automatically fix some problems. ``blacken-docs`` Automatically formats Python code in reStructuredText documentation and docstrings. @@ -65,26 +71,36 @@ To proceed, stage the new modifications and proceed with your Git commit. Running tests ============= -To test the library, run tox_, which tests the library the same way that the CI workflow does: +To run all Safir tests, run: -.. code-block:: sh +.. prompt:: bash - tox run + nox -s -To see a listing of test environments, run: +This tests the library in the same way that the CI workflow does. +You may wish to run the individual sessions (``lint``, ``typing``, ``test``, ``docs``, and ``docs-linkcheck``) when iterating on a specific change. +Consider using the ``-R`` flag when you haven't updated dependencies, as discussed below. -.. code-block:: sh +To see a listing of nox sessions: - tox list +.. prompt:: bash -tox will start a PostgreSQL container, which is required for some tests. + nox --list -To run a specific test or list of tests, you can add test file names (and any other pytest_ options) after ``--`` when executing the ``py`` tox environment. +To run a specific test or list of tests, you can add test file names (and any other pytest_ options) after ``--`` when executing the ``test`` nox session. For example: -.. code-block:: sh +.. prompt:: bash + + nox -s test -- safir/tests/database_test.py - tox run -e py -- tests/database_test.py +If you are interating on a specific test failure, you may want to pass the ``-R`` flag to skip the dependency installation step. +This will make nox run much faster, at the cost of not fixing out-of-date dependencies. +For example: + +.. prompt:: bash + + nox -Rs test -- safir/tests/database_test.py .. _dev-build-docs: @@ -95,12 +111,29 @@ Documentation is built with Sphinx_: .. _Sphinx: https://www.sphinx-doc.org/en/master/ -.. code-block:: sh +.. prompt:: bash - tox run -e docs + nox -s docs The build documentation is located in the :file:`docs/_build/html` directory. +Additional dependencies required for the documentation build should be added as development dependencies of the ``safir`` library, in :file:`safir/pyproject.toml`. + +Documentation builds are incremental, and generate and use cached descriptions of the internal Python APIs. +If you see errors in building the Python API documentation or have problems with changes to the documentation (particularly diagrams) not showing up, try a clean documentation build with: + +.. prompt:: bash + + nox -s docs-clean + +This will be slower, but it will ensure that the documentation build doesn't rely on any cached data. + +To check the documentation for broken links, run: + +.. code-block:: sh + + nox -s docs-linkcheck + .. _dev-change-log: Updating the change log @@ -136,7 +169,7 @@ Style guide Code ---- -- The code style follows :pep:`8`, though in practice lean on Black and isort to format the code for you. +- The code style follows :pep:`8`, though in practice lean on Ruff to format the code for you. - Use :pep:`484` type annotations. The ``tox run -e typing`` test environment, which runs mypy_, ensures that the project's types are consistent. diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 00000000..0238c11a --- /dev/null +++ b/noxfile.py @@ -0,0 +1,183 @@ +"""nox build configuration for Safir.""" + +import shutil +import sys +from pathlib import Path + +import nox +from nox.command import CommandFailed + +# Default sessions +nox.options.sessions = [ + "lint", + "typing", + "test", + "docs", + "docs-linkcheck", +] + +# Other nox defaults +nox.options.default_venv_backend = "uv" +nox.options.reuse_existing_virtualenvs = True + + +def _install(session: nox.Session) -> None: + """Install the application and all dependencies into the session.""" + session.install("--upgrade", "uv") + session.install("-e", "./safir[arq,db,dev,gcs,kubernetes,redis]") + + +def _install_dev(session: nox.Session, bin_prefix: str = "") -> None: + """Install the application and dev dependencies into the session.""" + python = f"{bin_prefix}python" + precommit = f"{bin_prefix}pre-commit" + + # Install dev dependencies + session.run(python, "-m", "pip", "install", "uv", external=True) + for args in (("nox[uv]", "pre-commit"), ("-e", "./safir[dev]")): + session.run(python, "-m", "uv", "pip", "install", *args, external=True) + + # Install pre-commit hooks + session.run(precommit, "install", external=True) + + +def _pytest( + session: nox.Session, directory: str, module: str, *, coverage: bool = True +) -> None: + """Run pytest for the given directory and module, if needed.""" + generic = [] + per_directory = [] + found_per_directory = False + for arg in session.posargs: + if arg.startswith("-"): + generic.append(arg) + elif arg.startswith(f"{directory}/"): + per_directory.append(arg.removeprefix(f"{directory}/")) + found_per_directory = True + elif "/" in arg and Path(arg).exists(): + found_per_directory = True + else: + generic.append(arg) + if not session.posargs or not found_per_directory or per_directory: + args = [] + if coverage: + args.extend([f"--cov={module}", "--cov-branch", "--cov-report="]) + with session.chdir(directory): + session.run( + "pytest", + *args, + *generic, + *per_directory, + ) + + +@nox.session(name="venv-init", venv_backend="virtualenv") +def venv_init(session: nox.Session) -> None: + """Set up a development venv. + + Create a venv in the current directory, replacing any existing one. + """ + session.run("python", "-m", "venv", ".venv", "--clear") + _install_dev(session, bin_prefix=".venv/bin/") + + print( + "\nTo activate this virtual env, run:\n\n\tsource .venv/bin/activate\n" + ) + + +@nox.session(name="init", python=False) +def init(session: nox.Session) -> None: + """Set up the development environment in the current virtual env.""" + _install_dev(session, bin_prefix="") + + +@nox.session +def lint(session: nox.Session) -> None: + """Run pre-commit hooks.""" + session.install("--upgrade", "pre-commit") + session.run("pre-commit", "run", "--all-files", *session.posargs) + + +@nox.session +def typing(session: nox.Session) -> None: + """Check type annotations with mypy.""" + _install(session) + session.run( + "mypy", + *session.posargs, + "noxfile.py", + "safir/src", + "safir/tests", + ) + + +@nox.session +def test(session: nox.Session) -> None: + """Run tests of Safir.""" + _install(session) + _pytest(session, "safir", "safir", coverage=True) + + +@nox.session +def docs(session: nox.Session) -> None: + """Build the documentation.""" + _install(session) + doctree_dir = (session.cache_dir / "doctrees").absolute() + with session.chdir("docs"): + session.run( + "sphinx-build", + "-W", + "--keep-going", + "-n", + "-T", + "-b", + "html", + "-d", + str(doctree_dir), + ".", + "./_build/html", + ) + + +@nox.session(name="docs-clean") +def docs_clean(session: nox.Session) -> None: + """Build the documentation without any cache.""" + if Path("docs/_build").exists(): + shutil.rmtree("docs/_build") + if Path("docs/api").exists(): + shutil.rmtree("docs/api") + docs(session) + + +@nox.session(name="docs-linkcheck") +def docs_linkcheck(session: nox.Session) -> None: + """Check documentation links.""" + _install(session) + doctree_dir = (session.cache_dir / "doctrees").absolute() + with session.chdir("docs"): + try: + session.run( + "sphinx-build", + "-W", + "--keep-going", + "-n", + "-T", + "-blinkcheck", + "-d", + str(doctree_dir), + ".", + "./_build/linkcheck", + ) + except CommandFailed: + output_path = Path("_build") / "linkcheck" / "output.txt" + if output_path.exists(): + sys.stdout.write(output_path.read_text()) + session.error("Link check reported errors") + + +@nox.session(name="update-deps") +def update_deps(session: nox.Session) -> None: + """Update pre-commit hooks.""" + session.install("--upgrade", "uv") + session.run("uv", "pip", "install", "pre-commit") + session.run("pre-commit", "autoupdate") diff --git a/pyproject.toml b/pyproject.toml index 55e53350..dd845afa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,124 +1,10 @@ -[project] -# https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ -name = "safir" -description = "The Rubin Observatory SQuaRE framework for FastAPI services." -license = {file = "LICENSE"} -readme= "README.md" -keywords = [ - "rubin", - "lsst", -] -# https://pypi.org/classifiers/ -classifiers = [ - "Development Status :: 5 - Production/Stable", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Intended Audience :: Developers", - "Natural Language :: English", - "Operating System :: POSIX", - "Typing :: Typed", -] -requires-python = ">=3.11" -dependencies = [ - "click<9", - "cryptography<44", - "fastapi<1", - "gidgethub<6", - "httpx>=0.20.0,<1", - "pydantic>2,<3", - "pydantic-core", - "starlette<1", - # 23.3.0 excluded due to https://github.com/hynek/structlog/issues/584 - "structlog>=21.2.0,!=23.3.0", -] -dynamic = ["version"] - -[project.optional-dependencies] -arq = [ - "arq>=0.23,<1" -] -db = [ - "asyncpg<1", - "sqlalchemy[asyncio]>=1.4.18,<3", -] -dev = [ - "asgi-lifespan", - "coverage[toml]", - "fastapi>=0.93.0", - "mypy", - "pre-commit", - "psycopg2", - "pytest", - "pytest-asyncio", - "redis>=5,<6", - "respx", - "scriv", - "sqlalchemy[mypy]", - "testcontainers[postgres,redis]", - "uvicorn", - # documentation - "documenteer[guide]>=1", - "autodoc_pydantic", -] -gcs = [ - "google-auth<3", - "google-cloud-storage<3" -] -kubernetes = [ - "kubernetes_asyncio<31" -] -redis = [ - "redis>4.5.2,<6", -] - -[[project.authors]] -name = "Association of Universities for Research in Astronomy, Inc. (AURA)" -email = "sqre-admin@lists.lsst.org" - -[project.urls] -Homepage = "https://safir.lsst.io" -Source = "https://github.com/lsst-sqre/safir" -"Change log" = "https://safir.lsst.io/changelog.html" -"Issue tracker" = "https://github.com/lsst-sqre/safir/issues" - -[build-system] -requires = [ - "setuptools>=61", - "wheel", - "setuptools_scm[toml]>=6.2" -] -build-backend = "setuptools.build_meta" - -[tool.setuptools_scm] - -[tool.coverage.run] -parallel = true -branch = true -source = ["safir"] - -[tool.coverage.paths] -source = ["src", ".tox/*/site-packages"] - -[tool.coverage.report] -show_missing = true -exclude_lines = [ - "pragma: no cover", - "def __repr__", - "if self.debug:", - "if settings.DEBUG", - "raise AssertionError", - "raise NotImplementedError", - "if 0:", - "if __name__ == .__main__.:", - "if TYPE_CHECKING:" -] +# This file contains only the tool configuration for linters. The +# configuration for the component Python projects may be found in the +# pyproject.toml files in subdirectories. [tool.black] line-length = 79 -target-version = ["py311"] +target-version = ["py312"] exclude = ''' /( \.eggs @@ -134,26 +20,6 @@ exclude = ''' # Use single-quoted strings so TOML treats the string like a Python r-string # Multi-line strings are implicitly treated by black as regular expressions -[tool.pytest.ini_options] -asyncio_mode = "strict" -filterwarnings = [ - # Google modules call a deprecated pkg_resources API. - "ignore:pkg_resources is deprecated as an API:DeprecationWarning", - "ignore:.*pkg_resources\\.declare_namespace:DeprecationWarning", - # Google modules use PyType_Spec in a deprecated way. - "ignore:Type google\\..*metaclass.* custom tp_new:DeprecationWarning", - # dateutil uses a deprecated datetime function. - "ignore:datetime.datetime.utcfromtimestamp:DeprecationWarning:dateutil.*", - # The point of this test is to test handling of datetime-naive UTC - # objects, which is what the deprecation warning is about. We want to - # continue doing this until the support has been removed entirely. - "ignore:datetime.datetime.utcnow:DeprecationWarning:tests.pydantic_test", -] -python_files = [ - "tests/*.py", - "tests/*/*.py" -] - [tool.mypy] disallow_untyped_defs = true disallow_incomplete_defs = true @@ -182,12 +48,26 @@ warn_untyped_fields = true extend = "ruff-shared.toml" [tool.ruff.lint.extend-per-file-ignores] -"src/safir/**" = [ +"noxfile.py" = [ + "T201", # print makes sense as output from nox rules +] +"safir/src/safir/**" = [ "N818", # Exception is correct in some cases, others are part of API ] -"src/safir/testing/**" = [ +"safir/src/safir/testing/**" = [ "S101", # test support functions are allowed to use assert ] +"*/tests/**" = [ + "C901", # tests are allowed to be complex, sometimes that's convenient + "D101", # tests don't need docstrings + "D103", # tests don't need docstrings + "PLR0915", # tests are allowed to be long, sometimes that's convenient + "PT012", # way too aggressive about limiting pytest.raises blocks + "S101", # tests should use assert + "S106", # tests are allowed to hard-code dummy passwords + "S301", # allow tests for whether code can be pickled + "SLF001", # tests are allowed to access private members +] [tool.ruff.lint.isort] known-first-party = ["safir", "tests"] diff --git a/safir/LICENSE b/safir/LICENSE new file mode 120000 index 00000000..ea5b6064 --- /dev/null +++ b/safir/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/safir/README.md b/safir/README.md new file mode 120000 index 00000000..32d46ee8 --- /dev/null +++ b/safir/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/safir/pyproject.toml b/safir/pyproject.toml new file mode 100644 index 00000000..5c93a0c6 --- /dev/null +++ b/safir/pyproject.toml @@ -0,0 +1,139 @@ +[project] +# https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ +name = "safir" +description = "The Rubin Observatory SQuaRE framework for FastAPI services." +license = {file = "LICENSE"} +readme= "README.md" +keywords = [ + "rubin", + "lsst", +] +# https://pypi.org/classifiers/ +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Intended Audience :: Developers", + "Natural Language :: English", + "Operating System :: POSIX", + "Typing :: Typed", +] +requires-python = ">=3.11" +dependencies = [ + "click<9", + "cryptography<44", + "fastapi<1", + "gidgethub<6", + "httpx>=0.20.0,<1", + "pydantic>2,<3", + "pydantic-core", + "starlette<1", + # 23.3.0 excluded due to https://github.com/hynek/structlog/issues/584 + "structlog>=21.2.0,!=23.3.0", +] +dynamic = ["version"] + +[project.optional-dependencies] +arq = [ + "arq>=0.23,<1" +] +db = [ + "asyncpg<1", + "sqlalchemy[asyncio]>=1.4.18,<3", +] +dev = [ + "asgi-lifespan", + "coverage[toml]", + "fastapi>=0.93.0", + "mypy", + "pre-commit", + "psycopg2", + "pytest", + "pytest-asyncio", + "pytest-cov", + "redis>=5,<6", + "respx", + "scriv", + "sqlalchemy[mypy]", + "testcontainers[postgres,redis]", + "uvicorn", + # documentation + "documenteer[guide]>=1", + "autodoc_pydantic", +] +gcs = [ + "google-auth<3", + "google-cloud-storage<3" +] +kubernetes = [ + "kubernetes_asyncio<31" +] +redis = [ + "redis>4.5.2,<6", +] + +[[project.authors]] +name = "Association of Universities for Research in Astronomy, Inc. (AURA)" +email = "sqre-admin@lists.lsst.org" + +[project.urls] +Homepage = "https://safir.lsst.io" +Source = "https://github.com/lsst-sqre/safir" +"Change log" = "https://safir.lsst.io/changelog.html" +"Issue tracker" = "https://github.com/lsst-sqre/safir/issues" + +[build-system] +requires = [ + "setuptools>=61", + "wheel", + "setuptools_scm[toml]>=6.2" +] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +root = ".." + +[tool.coverage.run] +parallel = true +branch = true +source = ["safir"] + +[tool.coverage.paths] +source = ["src", ".tox/*/site-packages"] + +[tool.coverage.report] +show_missing = true +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "if self.debug:", + "if settings.DEBUG", + "raise AssertionError", + "raise NotImplementedError", + "if 0:", + "if __name__ == .__main__.:", + "if TYPE_CHECKING:" +] + +[tool.pytest.ini_options] +asyncio_mode = "strict" +filterwarnings = [ + # Google modules call a deprecated pkg_resources API. + "ignore:pkg_resources is deprecated as an API:DeprecationWarning", + "ignore:.*pkg_resources\\.declare_namespace:DeprecationWarning", + # Google modules use PyType_Spec in a deprecated way. + "ignore:Type google\\..*metaclass.* custom tp_new:DeprecationWarning", + # dateutil uses a deprecated datetime function. + "ignore:datetime.datetime.utcfromtimestamp:DeprecationWarning:dateutil.*", + # The point of this test is to test handling of datetime-naive UTC + # objects, which is what the deprecation warning is about. We want to + # continue doing this until the support has been removed entirely. + "ignore:datetime.datetime.utcnow:DeprecationWarning:tests.pydantic_test", +] +python_files = [ + "tests/*.py", + "tests/*/*.py" +] diff --git a/src/safir/__init__.py b/safir/src/safir/__init__.py similarity index 100% rename from src/safir/__init__.py rename to safir/src/safir/__init__.py diff --git a/src/safir/arq.py b/safir/src/safir/arq.py similarity index 100% rename from src/safir/arq.py rename to safir/src/safir/arq.py diff --git a/src/safir/asyncio.py b/safir/src/safir/asyncio.py similarity index 100% rename from src/safir/asyncio.py rename to safir/src/safir/asyncio.py diff --git a/src/safir/click.py b/safir/src/safir/click.py similarity index 100% rename from src/safir/click.py rename to safir/src/safir/click.py diff --git a/src/safir/database/__init__.py b/safir/src/safir/database/__init__.py similarity index 100% rename from src/safir/database/__init__.py rename to safir/src/safir/database/__init__.py diff --git a/src/safir/database/_connection.py b/safir/src/safir/database/_connection.py similarity index 100% rename from src/safir/database/_connection.py rename to safir/src/safir/database/_connection.py diff --git a/src/safir/database/_datetime.py b/safir/src/safir/database/_datetime.py similarity index 100% rename from src/safir/database/_datetime.py rename to safir/src/safir/database/_datetime.py diff --git a/src/safir/database/_initialize.py b/safir/src/safir/database/_initialize.py similarity index 100% rename from src/safir/database/_initialize.py rename to safir/src/safir/database/_initialize.py diff --git a/src/safir/database/_retry.py b/safir/src/safir/database/_retry.py similarity index 100% rename from src/safir/database/_retry.py rename to safir/src/safir/database/_retry.py diff --git a/src/safir/datetime.py b/safir/src/safir/datetime.py similarity index 100% rename from src/safir/datetime.py rename to safir/src/safir/datetime.py diff --git a/src/safir/dependencies/__init__.py b/safir/src/safir/dependencies/__init__.py similarity index 100% rename from src/safir/dependencies/__init__.py rename to safir/src/safir/dependencies/__init__.py diff --git a/src/safir/dependencies/arq.py b/safir/src/safir/dependencies/arq.py similarity index 100% rename from src/safir/dependencies/arq.py rename to safir/src/safir/dependencies/arq.py diff --git a/src/safir/dependencies/db_session.py b/safir/src/safir/dependencies/db_session.py similarity index 100% rename from src/safir/dependencies/db_session.py rename to safir/src/safir/dependencies/db_session.py diff --git a/src/safir/dependencies/gafaelfawr.py b/safir/src/safir/dependencies/gafaelfawr.py similarity index 100% rename from src/safir/dependencies/gafaelfawr.py rename to safir/src/safir/dependencies/gafaelfawr.py diff --git a/src/safir/dependencies/http_client.py b/safir/src/safir/dependencies/http_client.py similarity index 100% rename from src/safir/dependencies/http_client.py rename to safir/src/safir/dependencies/http_client.py diff --git a/src/safir/dependencies/logger.py b/safir/src/safir/dependencies/logger.py similarity index 100% rename from src/safir/dependencies/logger.py rename to safir/src/safir/dependencies/logger.py diff --git a/src/safir/fastapi.py b/safir/src/safir/fastapi.py similarity index 100% rename from src/safir/fastapi.py rename to safir/src/safir/fastapi.py diff --git a/src/safir/gcs.py b/safir/src/safir/gcs.py similarity index 100% rename from src/safir/gcs.py rename to safir/src/safir/gcs.py diff --git a/src/safir/github/__init__.py b/safir/src/safir/github/__init__.py similarity index 100% rename from src/safir/github/__init__.py rename to safir/src/safir/github/__init__.py diff --git a/src/safir/github/_client.py b/safir/src/safir/github/_client.py similarity index 100% rename from src/safir/github/_client.py rename to safir/src/safir/github/_client.py diff --git a/src/safir/github/models.py b/safir/src/safir/github/models.py similarity index 100% rename from src/safir/github/models.py rename to safir/src/safir/github/models.py diff --git a/src/safir/github/webhooks.py b/safir/src/safir/github/webhooks.py similarity index 100% rename from src/safir/github/webhooks.py rename to safir/src/safir/github/webhooks.py diff --git a/src/safir/kubernetes.py b/safir/src/safir/kubernetes.py similarity index 100% rename from src/safir/kubernetes.py rename to safir/src/safir/kubernetes.py diff --git a/src/safir/logging.py b/safir/src/safir/logging.py similarity index 100% rename from src/safir/logging.py rename to safir/src/safir/logging.py diff --git a/src/safir/metadata.py b/safir/src/safir/metadata.py similarity index 100% rename from src/safir/metadata.py rename to safir/src/safir/metadata.py diff --git a/src/safir/middleware/__init__.py b/safir/src/safir/middleware/__init__.py similarity index 100% rename from src/safir/middleware/__init__.py rename to safir/src/safir/middleware/__init__.py diff --git a/src/safir/middleware/ivoa.py b/safir/src/safir/middleware/ivoa.py similarity index 100% rename from src/safir/middleware/ivoa.py rename to safir/src/safir/middleware/ivoa.py diff --git a/src/safir/middleware/x_forwarded.py b/safir/src/safir/middleware/x_forwarded.py similarity index 100% rename from src/safir/middleware/x_forwarded.py rename to safir/src/safir/middleware/x_forwarded.py diff --git a/src/safir/models.py b/safir/src/safir/models.py similarity index 100% rename from src/safir/models.py rename to safir/src/safir/models.py diff --git a/src/safir/py.typed b/safir/src/safir/py.typed similarity index 100% rename from src/safir/py.typed rename to safir/src/safir/py.typed diff --git a/src/safir/pydantic.py b/safir/src/safir/pydantic.py similarity index 100% rename from src/safir/pydantic.py rename to safir/src/safir/pydantic.py diff --git a/src/safir/redis.py b/safir/src/safir/redis.py similarity index 100% rename from src/safir/redis.py rename to safir/src/safir/redis.py diff --git a/src/safir/slack/__init__.py b/safir/src/safir/slack/__init__.py similarity index 100% rename from src/safir/slack/__init__.py rename to safir/src/safir/slack/__init__.py diff --git a/src/safir/slack/blockkit.py b/safir/src/safir/slack/blockkit.py similarity index 100% rename from src/safir/slack/blockkit.py rename to safir/src/safir/slack/blockkit.py diff --git a/src/safir/slack/webhook.py b/safir/src/safir/slack/webhook.py similarity index 100% rename from src/safir/slack/webhook.py rename to safir/src/safir/slack/webhook.py diff --git a/src/safir/testing/__init__.py b/safir/src/safir/testing/__init__.py similarity index 100% rename from src/safir/testing/__init__.py rename to safir/src/safir/testing/__init__.py diff --git a/src/safir/testing/gcs.py b/safir/src/safir/testing/gcs.py similarity index 100% rename from src/safir/testing/gcs.py rename to safir/src/safir/testing/gcs.py diff --git a/src/safir/testing/kubernetes.py b/safir/src/safir/testing/kubernetes.py similarity index 99% rename from src/safir/testing/kubernetes.py rename to safir/src/safir/testing/kubernetes.py index b10a0eec..22b15d7f 100644 --- a/src/safir/testing/kubernetes.py +++ b/safir/src/safir/testing/kubernetes.py @@ -9,7 +9,7 @@ from collections import defaultdict from collections.abc import AsyncIterator, Callable, Iterator from datetime import timedelta -from typing import Any +from typing import Any, Protocol from unittest.mock import AsyncMock, Mock, patch from kubernetes_asyncio import client, config @@ -44,7 +44,6 @@ V1ServiceList, V1Status, ) -from typing_extensions import Protocol from ..asyncio import AsyncMultiQueue diff --git a/src/safir/testing/slack.py b/safir/src/safir/testing/slack.py similarity index 100% rename from src/safir/testing/slack.py rename to safir/src/safir/testing/slack.py diff --git a/src/safir/testing/uvicorn.py b/safir/src/safir/testing/uvicorn.py similarity index 100% rename from src/safir/testing/uvicorn.py rename to safir/src/safir/testing/uvicorn.py diff --git a/tests/__init__.py b/safir/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to safir/tests/__init__.py diff --git a/tests/arq_test.py b/safir/tests/arq_test.py similarity index 100% rename from tests/arq_test.py rename to safir/tests/arq_test.py diff --git a/tests/asyncio_test.py b/safir/tests/asyncio_test.py similarity index 100% rename from tests/asyncio_test.py rename to safir/tests/asyncio_test.py diff --git a/tests/click_test.py b/safir/tests/click_test.py similarity index 100% rename from tests/click_test.py rename to safir/tests/click_test.py diff --git a/tests/conftest.py b/safir/tests/conftest.py similarity index 100% rename from tests/conftest.py rename to safir/tests/conftest.py diff --git a/tests/database_test.py b/safir/tests/database_test.py similarity index 100% rename from tests/database_test.py rename to safir/tests/database_test.py diff --git a/tests/datetime_test.py b/safir/tests/datetime_test.py similarity index 100% rename from tests/datetime_test.py rename to safir/tests/datetime_test.py diff --git a/tests/dependencies/__init__.py b/safir/tests/dependencies/__init__.py similarity index 100% rename from tests/dependencies/__init__.py rename to safir/tests/dependencies/__init__.py diff --git a/tests/dependencies/arq_test.py b/safir/tests/dependencies/arq_test.py similarity index 100% rename from tests/dependencies/arq_test.py rename to safir/tests/dependencies/arq_test.py diff --git a/tests/dependencies/db_session_test.py b/safir/tests/dependencies/db_session_test.py similarity index 100% rename from tests/dependencies/db_session_test.py rename to safir/tests/dependencies/db_session_test.py diff --git a/tests/dependencies/gafaelfawr_test.py b/safir/tests/dependencies/gafaelfawr_test.py similarity index 100% rename from tests/dependencies/gafaelfawr_test.py rename to safir/tests/dependencies/gafaelfawr_test.py diff --git a/tests/dependencies/http_client_test.py b/safir/tests/dependencies/http_client_test.py similarity index 100% rename from tests/dependencies/http_client_test.py rename to safir/tests/dependencies/http_client_test.py diff --git a/tests/dependencies/logger_test.py b/safir/tests/dependencies/logger_test.py similarity index 100% rename from tests/dependencies/logger_test.py rename to safir/tests/dependencies/logger_test.py diff --git a/tests/fastapi_test.py b/safir/tests/fastapi_test.py similarity index 100% rename from tests/fastapi_test.py rename to safir/tests/fastapi_test.py diff --git a/tests/gcs_test.py b/safir/tests/gcs_test.py similarity index 100% rename from tests/gcs_test.py rename to safir/tests/gcs_test.py diff --git a/tests/github/__init__.py b/safir/tests/github/__init__.py similarity index 100% rename from tests/github/__init__.py rename to safir/tests/github/__init__.py diff --git a/tests/github/data/webhooks/check_run_created.json b/safir/tests/github/data/webhooks/check_run_created.json similarity index 100% rename from tests/github/data/webhooks/check_run_created.json rename to safir/tests/github/data/webhooks/check_run_created.json diff --git a/tests/github/data/webhooks/check_suite_completed.json b/safir/tests/github/data/webhooks/check_suite_completed.json similarity index 100% rename from tests/github/data/webhooks/check_suite_completed.json rename to safir/tests/github/data/webhooks/check_suite_completed.json diff --git a/tests/github/data/webhooks/installation.json b/safir/tests/github/data/webhooks/installation.json similarity index 100% rename from tests/github/data/webhooks/installation.json rename to safir/tests/github/data/webhooks/installation.json diff --git a/tests/github/data/webhooks/installation_repositories.json b/safir/tests/github/data/webhooks/installation_repositories.json similarity index 100% rename from tests/github/data/webhooks/installation_repositories.json rename to safir/tests/github/data/webhooks/installation_repositories.json diff --git a/tests/github/data/webhooks/pull_request_event.json b/safir/tests/github/data/webhooks/pull_request_event.json similarity index 100% rename from tests/github/data/webhooks/pull_request_event.json rename to safir/tests/github/data/webhooks/pull_request_event.json diff --git a/tests/github/data/webhooks/push_event.json b/safir/tests/github/data/webhooks/push_event.json similarity index 100% rename from tests/github/data/webhooks/push_event.json rename to safir/tests/github/data/webhooks/push_event.json diff --git a/tests/github/webhooks_test.py b/safir/tests/github/webhooks_test.py similarity index 100% rename from tests/github/webhooks_test.py rename to safir/tests/github/webhooks_test.py diff --git a/tests/kubernetes_test.py b/safir/tests/kubernetes_test.py similarity index 100% rename from tests/kubernetes_test.py rename to safir/tests/kubernetes_test.py diff --git a/tests/logging_test.py b/safir/tests/logging_test.py similarity index 100% rename from tests/logging_test.py rename to safir/tests/logging_test.py diff --git a/tests/metadata_test.py b/safir/tests/metadata_test.py similarity index 100% rename from tests/metadata_test.py rename to safir/tests/metadata_test.py diff --git a/tests/middleware/__init__.py b/safir/tests/middleware/__init__.py similarity index 100% rename from tests/middleware/__init__.py rename to safir/tests/middleware/__init__.py diff --git a/tests/middleware/ivoa_test.py b/safir/tests/middleware/ivoa_test.py similarity index 100% rename from tests/middleware/ivoa_test.py rename to safir/tests/middleware/ivoa_test.py diff --git a/tests/middleware/x_forwarded_test.py b/safir/tests/middleware/x_forwarded_test.py similarity index 100% rename from tests/middleware/x_forwarded_test.py rename to safir/tests/middleware/x_forwarded_test.py diff --git a/tests/models_test.py b/safir/tests/models_test.py similarity index 100% rename from tests/models_test.py rename to safir/tests/models_test.py diff --git a/tests/pydantic_test.py b/safir/tests/pydantic_test.py similarity index 100% rename from tests/pydantic_test.py rename to safir/tests/pydantic_test.py diff --git a/tests/redis_test.py b/safir/tests/redis_test.py similarity index 100% rename from tests/redis_test.py rename to safir/tests/redis_test.py diff --git a/tests/safir_test.py b/safir/tests/safir_test.py similarity index 100% rename from tests/safir_test.py rename to safir/tests/safir_test.py diff --git a/tests/slack/__init__.py b/safir/tests/slack/__init__.py similarity index 100% rename from tests/slack/__init__.py rename to safir/tests/slack/__init__.py diff --git a/tests/slack/blockkit_test.py b/safir/tests/slack/blockkit_test.py similarity index 100% rename from tests/slack/blockkit_test.py rename to safir/tests/slack/blockkit_test.py diff --git a/tests/slack/webhook_test.py b/safir/tests/slack/webhook_test.py similarity index 100% rename from tests/slack/webhook_test.py rename to safir/tests/slack/webhook_test.py diff --git a/tests/testing/__init__.py b/safir/tests/testing/__init__.py similarity index 100% rename from tests/testing/__init__.py rename to safir/tests/testing/__init__.py diff --git a/tests/testing/conftest.py b/safir/tests/testing/conftest.py similarity index 100% rename from tests/testing/conftest.py rename to safir/tests/testing/conftest.py diff --git a/tests/testing/gcs_test.py b/safir/tests/testing/gcs_test.py similarity index 100% rename from tests/testing/gcs_test.py rename to safir/tests/testing/gcs_test.py diff --git a/tests/testing/kubernetes_test.py b/safir/tests/testing/kubernetes_test.py similarity index 100% rename from tests/testing/kubernetes_test.py rename to safir/tests/testing/kubernetes_test.py