diff --git a/.github/workflows/bump.yml b/.github/workflows/bump.yml index beac85248..e71604e34 100644 --- a/.github/workflows/bump.yml +++ b/.github/workflows/bump.yml @@ -8,17 +8,21 @@ on: required: false rule: type: choice - description: Version bump level + description: Version bump level (ignored if explicit version is given) required: true options: - - prerelease - - "prerelease --next-phase" + - dev - prepatch - preminor - premajor - patch - minor - major + version: + type: string + description: Explicit version (overrides rule), e.g. "X.Y.Zb0" to enter feature freeze. + required: false + default: "" jobs: Bump: @@ -27,4 +31,4 @@ jobs: secrets: inherit with: dry-run: ${{ inputs.dry-run }} - rule: ${{ inputs.rule }} + rule: ${{ inputs.version != '' && inputs.version || inputs.rule }} diff --git a/.github/workflows/bump_on_dependency.yml b/.github/workflows/bump_on_dependency.yml index fa3bf51b3..974fd2d9f 100644 --- a/.github/workflows/bump_on_dependency.yml +++ b/.github/workflows/bump_on_dependency.yml @@ -5,7 +5,7 @@ on: jobs: Bump: - name: Bump prerelease version + name: Bump dev version if: > github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' && @@ -16,6 +16,5 @@ jobs: uses: AstarVienna/DevOps/.github/workflows/bump.yml@main secrets: inherit with: - rule: prerelease + rule: dev branch: main - diff --git a/.github/workflows/notebooks_dispatch.yml b/.github/workflows/notebooks_dispatch.yml index 70626c3c1..869c6daac 100644 --- a/.github/workflows/notebooks_dispatch.yml +++ b/.github/workflows/notebooks_dispatch.yml @@ -23,20 +23,20 @@ jobs: # is more useful than 5 green and one red. The others can be enabled # once we fix the problem properly. # os: [ubuntu-latest, windows-latest, macos-latest] - # python-version: ['3.8', '3.11'] + # python-version: ['3.12', '3.14'] os: [ubuntu-latest] - python-version: ["3.11"] + python-version: ["3.12"] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Install poetry shell: bash - run: pipx install poetry==2.1.4 + run: pipx install poetry==2.4.1 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml new file mode 100644 index 000000000..705405e4f --- /dev/null +++ b/.github/workflows/prepare_release.yml @@ -0,0 +1,47 @@ +name: Prepare release +on: + workflow_dispatch: + inputs: + rule: + type: choice + description: Version bump level for the release (ignored if explicit version is given) + required: true + options: + - patch + - minor + - major + - prepatch + - preminor + - premajor + version: + type: string + description: > + Explicit release version (overrides rule), e.g. "X.Y.Zb0" to tag the feature freeze. + required: false + default: "" + notes: + type: string + description: > + Optional manual release notes section, shown above the + auto-generated notes. Can also be added or edited in the + draft release before publishing. + required: false + default: "" + previous-tag: + type: string + description: > + Optional comparison base for auto-generated notes. Only needed + for backport releases from maintenance branches (e.g. "v2.1.0" + when releasing 2.1.1 while 2.5.0 already exists). + required: false + default: "" + +jobs: + Prepare: + name: Prepare release + uses: AstarVienna/DevOps/.github/workflows/prepare_release.yml@main + secrets: inherit + with: + rule: ${{ inputs.version != '' && inputs.version || inputs.rule }} + notes: ${{ inputs.notes }} + previous-tag: ${{ inputs.previous-tag }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b1bc8e667..d2dfa5b67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,16 +19,24 @@ jobs: name: Offline tests uses: AstarVienna/DevOps/.github/workflows/tests.yml@main secrets: inherit + with: + python-versions: '["3.12", "3.13", "3.14"]' + python-min: "3.12" # limited versions for macOS only + python-max: "3.14" # limited versions for macOS only call-webtests: name: Network tests uses: AstarVienna/DevOps/.github/workflows/webtests.yml@main secrets: inherit + with: + python-versions: '["3.12", "3.14"]' call-updated-tests: name: Tests with updated dependencies uses: AstarVienna/DevOps/.github/workflows/updated_tests.yml@main secrets: inherit + with: + python-versions: '["3.12", "3.14"]' call-notebooks-with-clone: name: Notebook tests diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 389c1d7a1..4a10c86b7 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,9 +5,9 @@ version: 2 build: - os: "ubuntu-24.04" + os: "ubuntu-26.04" tools: - python: "3.11" + python: "3.14" jobs: post_checkout: - git clone -n --depth=1 --filter=tree:0 https://github.com/AstarVienna/irdb ./docs/source/examples/inst_pkgs diff --git a/docs/to-do-list.txt b/docs/to-do-list.txt deleted file mode 100644 index 45d661928..000000000 --- a/docs/to-do-list.txt +++ /dev/null @@ -1,107 +0,0 @@ -ScopeSim Development Plan / Wishlist -==================================== - -ScopeSim --------- -- Finish update of spectroscopy mode to incorporate the core code from SpecCADO - WHY: Speeds up SLIT mode, enables expansion to IFU and MOS spectrographs - WHAT: Implement spatial-spectral Field-of-View objects - WHAT: Re-write of FOV-Manager class to accomodate 2+1D FOV objects - -- Simplify Effect object API - WHY: Makes "custom" effect objects easier to write for 3rd parties (e.g. JU Pott's astrometry group) - WHAT: Remove the "fov_grid" method used by FOV-Manager. Replace it with a further apply_to loop when initialising the in-memory instrument model - -- Add 3D Detector-Cube object - WHY: Enables very quick "science grade" spectroscopy data products to be produced - WHY: Paves the way for ETC-mode for ScopeSim - WHAT: Write new DetectorCube class, which piggy-backs the current Detector class - -- Add ETC mode - WHY: Quick generation of ETC table data for delivery to ESO (and whomever else wants it) - WHY: Increases future usability by astronomers when writing observing proposals - WHAT: Add functions for determining SNR through photometry, line integration, or per-pixel - WHAT: Add DetectorETC class, which contains flux and noise images, and returns a table of SNR values - -- Add simple Coronographic Effects (as much as possible) - WHY: Elephant in the room. Needs to be included. - WHAT: An Effect wrapper for a grid of PSFs for different coronographic conditions - -- Add "observatory like" functionality (specifiy RA, Dec, obs date) - WHY: Most often requested feature is to add RA and Dec to observing parameters. - WHY: Atmospheric conditions change with Zenith distance, which is related to RA and Dec - WHY: Helps astronomers better conceptualise their observations - WHAT: ScopeSim will pull in a list of objects from Simbad and add these to the background of the images - WHAT: Link skycalc Effect wrapper to the RA and Dec (i.e. Zenith Distance) parameters - -- Link to external packages like TIPTOP (generic anayltical AO PSF realisations) and PYXEL (generic detector noise simualtions) - WHY: For more accurate realistions of the most important external noise sources - WHAT: Write an interface standard for these two packages. Possibly liase with the developers so that the packages can deliver "ScopeSim-Ready" objects - -- Remove dependency on astropy units - WHY: They are slow - WHAT: Define a set of internal units for Scopesim. Replace all astropy.unit references with internally consistent units propogation - -- Remove dependency on astropy HDU objects - WHY: They are slow. Too many internal checks on the header keywords. Factor of 10x speed increase when using ScopeSim's interal "PoorMansHeader" object - WHAT: Expand PoorMansHeader to cover all use cases, add minimal units checks, replace all occurences of astropy HDU objects - -- Remove dependency on Synphot spectra objects - WHY: They are slow. Based on astropy interpolation objects and astropy units. Too many checks. - WHAT: Write a new high-speed spectral interpolatio class based on the scipy.interpolate objects. - -- Add time-dependency to the observation sequences - WHY: To allow realistic astrometry observations - WHAT: Add a time parameter to the on-sky Source objects - WHAT: Add a OBS_DATE parameter to the OpticalTrain class. This places the objects in the Field-of-View as seen on the date of the observation - - -ScopeSim_Templates ------------------- -- Add ArtPop wrapper. ArtPop is a better (3rd-party) generator for (old and new) stellar population ScopeSim-Source objects. - WHY: Allows users to make more realistic on-sky targets using isochrone libraries. - WHAT: Wrap the ArtPop package into a ScopeSim_Templates sub-module - WHAT: Code and submit a pull request to the ArtPop developers to enable ArtPop to generate ScopeSim-like Source objects - -- Add Vela simualtion wrapper. Vela is a set of high-res simulated galaxies observed over cosmic time - WHY: Allows users to add realisitc galaxies to their images with spatial resolutions better than MICADO for any redshift. Useful for imaging feasibility studies - WHAT: Write a wrapper sub-module that fetches data from the Vela server (or from a copy hosted at Uni-Vienna) - -- Add suite of test objects for pipeline development projects (MICADO, METIS) - WHY: A designated set of pipeline-useful objects helps standardise the objects included (or excluded) from pipeline testing - WHAT: Add the recipes developed by Hugo Buddelmeijer to a MICADO sub-package of ScopeSim_Templates - -- Add stellar population astronometric helper functions - WHY: Makes running astrometric science feasibiltiy cases easier for the users - WHAT: Add the code written by JU-Pott's group for astrometric simulations to the ScopeSim-Templates "stars" sub-package - - -IRDB ----- -- Add a MICADO_ETC package - WHAT: Add consolidated transmission curves, PSFs, detector characteristics to enable high-speed windowed simuations - -- Updates to MICADO, MICADO_Sci, MORFEO, ELT, Armazones packages as new data becomes available - WHAT: Add new modes, updated values and data files - -- Automatic documentation for each of the packages - WHY: Helps authors check whether the package data is contained in the proper format - WHAT: Code to build a documentation page (on RTFD) for each package upon pushing to GitHub - -- Automatic file consistency testing - WHY: Automatic tests let the author know if the file(s) they add will break or significantly alter the expected simulation outputs - WHAT: Code to check units and min, max values - -- Automated instrument performance estimates - WHY: Lets users and authors know what the expected performance of the instrument will be, based on the data files and settings included in the packages - WHAT: Definition of how to determine instrument performance - WHAT: Code for running and extracting the instrument performance parameters - - -Documentation and Testing -------------------------- -- Update and expand astronomy Use-case documentation -- Update and expand Developer use-case documentation -- Update and expand Functional interface (API) documentation -- Write a series of iPython Notebooks as "learning-by-doing" guides -- Maybe a series of youtube videos describing what is happening and how to take advantage of the saved internal information in ScopeSim diff --git a/poetry.lock b/poetry.lock index 05022a92b..ebb77fc36 100644 --- a/poetry.lock +++ b/poetry.lock @@ -33,23 +33,20 @@ files = [ [[package]] name = "anisocado" -version = "0.5.0" +version = "0.6.0" description = "Generate off-axis SCAO PSFs for the ELT" optional = false -python-versions = "<3.15,>=3.10" +python-versions = "<3.15,>=3.12" groups = ["main"] files = [ - {file = "anisocado-0.5.0-py3-none-any.whl", hash = "sha256:d96747ea0661779cd317f05aa4a15fd1f2d896be0eef8246b1718ce23d70d1b0"}, - {file = "anisocado-0.5.0.tar.gz", hash = "sha256:f386b68b89b586c7835d2937dff80f64d3d46f32a369701abed7902220dc173a"}, + {file = "anisocado-0.6.0-py3-none-any.whl", hash = "sha256:711f55a9ca4dc43e87d22abf2b91756cc14b5e46769d87e7df448df5e7257f84"}, + {file = "anisocado-0.6.0.tar.gz", hash = "sha256:c8dd96a5d57fc87d6d9f9b221a723a00218cecdc4942a5a3477f76bb62ff0aa2"}, ] [package.dependencies] -astropy = ">=6.1.7,<8.0.0" -matplotlib = ">=3.10.1,<4.0.0" -numpy = [ - {version = ">=1.26.4,<2.3.0", markers = "python_version >= \"3.10\" and python_version < \"3.13\""}, - {version = ">=2.2.6,<3.0.0", markers = "python_version >= \"3.13\""}, -] +astropy = ">=7.0.2,<9.0.0" +matplotlib = ">=3.10.9,<4.0.0" +numpy = ">=2.2.6,<3.0.0" [[package]] name = "anyio" @@ -64,7 +61,6 @@ files = [ ] [package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" @@ -167,14 +163,14 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "astar-utils" -version = "0.5.3" +version = "0.7.0" description = "Contains commonly-used utilities for AstarVienna's projects." optional = false -python-versions = "<3.15,>=3.10" +python-versions = "<3.15,>=3.12" groups = ["main"] files = [ - {file = "astar_utils-0.5.3-py3-none-any.whl", hash = "sha256:2b595b3c5acc20888e6beb8cffcbb5999d17311a358ba97c75d331883a6e0e8e"}, - {file = "astar_utils-0.5.3.tar.gz", hash = "sha256:57423f4c5eeece04b7d92ca6d1f8923f1a25ccfa972f6ddb8194dbcd9197533b"}, + {file = "astar_utils-0.7.0-py3-none-any.whl", hash = "sha256:f5b3a9e63ba54b75824dfe37fa885680c835c6a25976684f298a8fd517743872"}, + {file = "astar_utils-0.7.0.tar.gz", hash = "sha256:049e837889ba759df71b707ca282ab0737bf990caddea7854f9f06e1d94326a7"}, ] [package.dependencies] @@ -184,68 +180,52 @@ pyyaml = ">=6.0.3,<7.0.0" [[package]] name = "astropy" -version = "6.1.7" +version = "7.2.2" description = "Astronomy and astrophysics core library" optional = false -python-versions = ">=3.10" +python-versions = ">=3.11" groups = ["main"] files = [ - {file = "astropy-6.1.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be954c5f7707a089609053665aeb76493b79e5c4753c39486761bc6d137bf040"}, - {file = "astropy-6.1.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5e48df5ab2e3e521e82a7233a4b1159d071e64e6cbb76c45415dc68d3b97af1"}, - {file = "astropy-6.1.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55c78252633c644361e2f7092d71f80ef9c2e6649f08d97711d9f19af514aedc"}, - {file = "astropy-6.1.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985e5e74489d23f1a11953b6b283fccde3f46cb6c68fee4f7228e5f6d8350ba9"}, - {file = "astropy-6.1.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc2ea28ed41a3d92c39b1481d9c5be016ae58d68f144f3fd8cecffe503525bab"}, - {file = "astropy-6.1.7-cp310-cp310-win32.whl", hash = "sha256:4e4badadd8dfa5dca08fd86e9a50a3a91af321975859f5941579e6b7ce9ba199"}, - {file = "astropy-6.1.7-cp310-cp310-win_amd64.whl", hash = "sha256:8d7f6727689288ee08fc0a4a297fc7e8089d01718321646bd00fea0906ad63dc"}, - {file = "astropy-6.1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:09edca01276ee63f7b2ff511da9bfb432068ba3242e27ef27d76e5a171087b7e"}, - {file = "astropy-6.1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:072f62a67992393beb016dc80bee8fb994fda9aa69e945f536ed8ac0e51291e6"}, - {file = "astropy-6.1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2706156d3646f9c9a7fc810475d8ab0df4c717beefa8326552576a0f8ddca20"}, - {file = "astropy-6.1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcd99e627692f8e58bb3097d330bfbd109a22e00dab162a67f203b0a0601ad2c"}, - {file = "astropy-6.1.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b0ebbcb637b2e9bcb73011f2b7890d7a3f5a41b66ccaad7c28f065e81e28f0b2"}, - {file = "astropy-6.1.7-cp311-cp311-win32.whl", hash = "sha256:192b12ede49cd828362ab1a6ede2367fe203f4d851804ec22fa92e009a524281"}, - {file = "astropy-6.1.7-cp311-cp311-win_amd64.whl", hash = "sha256:3cac64bcdf570c947019bd2bc96711eeb2c7763afe192f18c9551e52a6c296b2"}, - {file = "astropy-6.1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2a8bcbb1306052cc38c9eed2c9331bfafe2582b499a7321946abf74b26eb256"}, - {file = "astropy-6.1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eaf88878684f9d31aff36475c90d101f4cff22fdd4fd50098d9950fd56994df7"}, - {file = "astropy-6.1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb8cd231e53556e4eebe0393ea95a8cea6b2ff4187c95ac4ff8b17e7a8da823"}, - {file = "astropy-6.1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ad36334d138a4f71d6fdcf225a98ad1dad6c343da4362d5a47a71f5c9da3ca9"}, - {file = "astropy-6.1.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dd731c526869d0c68507be7b31dd10871b7c44d310bb5495476505560c83cd33"}, - {file = "astropy-6.1.7-cp312-cp312-win32.whl", hash = "sha256:662bacd7ae42561e038cbd85eea3b749308cf3575611a745b60f034d3350c97a"}, - {file = "astropy-6.1.7-cp312-cp312-win_amd64.whl", hash = "sha256:5b4d02a98a0bf91ff7fd4ef0bd0ecca83c9497338cb88b61ec9f971350688222"}, - {file = "astropy-6.1.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbeaf04427987c0c6fa2e579eb40011802b06fba6b3a7870e082d5c693564e1b"}, - {file = "astropy-6.1.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab6e88241a14185b9404b02246329185b70292984aa0616b20a0628dfe4f4ebb"}, - {file = "astropy-6.1.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0529c75565feaabb629946806b4763ae7b02069aeff4c3b56a69e8a9e638500"}, - {file = "astropy-6.1.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5ec347631da77573fc729ba04e5d89a3bc94500bf6037152a2d0f9965ae1ce"}, - {file = "astropy-6.1.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc496f87aaccaa5c6624acc985b8770f039c5bbe74b120c8ed7bad3698e24e1b"}, - {file = "astropy-6.1.7-cp313-cp313-win32.whl", hash = "sha256:b1e01d534383c038dbf8664b964fa4ea818c7419318830d3c732c750c64115c6"}, - {file = "astropy-6.1.7-cp313-cp313-win_amd64.whl", hash = "sha256:af08cf2b0368f1ea585eb26a55d99a2de9e9b0bd30aba84b5329059c3ec33590"}, - {file = "astropy-6.1.7.tar.gz", hash = "sha256:a405ac186306b6cb152e6df2f7444ab8bd764e4127d7519da1b3ae4dd65357ef"}, + {file = "astropy-7.2.2-cp311-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b082021721761c15e23cc26bca0efdd74e8fce534ace188abff15e95e1144fa9"}, + {file = "astropy-7.2.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:7cceacf26c1492a6234e63a2b5a638b55d4ae88b5d899912b4c357bb7ea8ee03"}, + {file = "astropy-7.2.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b0a168ec6b4fa50344b61b0b2f372695f601bdf0ea627b27ec23d9543ef5206"}, + {file = "astropy-7.2.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82b4270397a8f62ca7d0d4e268c1412b83a401c148f7525ac75ae7fd85790856"}, + {file = "astropy-7.2.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:54cb7e5a866052e075596ac42fabf6166c5e9b49924d0ff5ec3c6c3a9db1592b"}, + {file = "astropy-7.2.2-cp311-abi3-win32.whl", hash = "sha256:c5aa4f94b0761b371157ee56c7779ace3c72de94680dabd8fe72ff6f8aebd700"}, + {file = "astropy-7.2.2-cp311-abi3-win_amd64.whl", hash = "sha256:3e3fd54268be95a5b13113bbe380b4f8b2b6431bd58a6853edb82d4528766b8f"}, + {file = "astropy-7.2.2-cp311-abi3-win_arm64.whl", hash = "sha256:30f989a0d2cba6273d4645a4274e4d29aef842832c14a3cf622bdedb7aa05c6d"}, + {file = "astropy-7.2.2.tar.gz", hash = "sha256:d48d6025636fa1330594603b9c01345561b6da404e2115852a9136db13f455cd"}, ] [package.dependencies] -astropy-iers-data = ">=0.2024.10.28.0.34.7" -numpy = ">=1.23" -packaging = ">=19.0" +astropy-iers-data = ">=0.2026.6.22.1.23.34" +numpy = ">=1.24,<2.7" +packaging = ">=22.0.0" pyerfa = ">=2.0.1.1" -PyYAML = ">=3.13" +PyYAML = ">=6.0.0" [package.extras] -all = ["asdf-astropy (>=0.3)", "astropy[recommended]", "astropy[typing]", "beautifulsoup4", "bleach", "bottleneck", "certifi", "dask[array]", "fsspec[http] (>=2023.4.0)", "h5py", "html5lib", "ipython (>=4.2)", "jplephem", "mpmath", "pandas", "pre-commit", "pyarrow (>=7.0.0)", "pytest (>=7.0)", "pytz", "s3fs (>=2023.4.0)", "sortedcontainers"] -docs = ["Jinja2 (>=3.1.3)", "astropy[recommended]", "matplotlib (>=3.9.1)", "numpy (<2.0)", "pytest (>=7.0)", "sphinx", "sphinx-astropy[confv2] (>=1.9.1)", "sphinx-changelog (>=1.2.0)", "sphinx_design", "sphinxcontrib-globalsubs (>=0.1.1)", "tomli ; python_version < \"3.11\""] -recommended = ["matplotlib (>=3.5.0,!=3.5.2)", "scipy (>=1.8)"] -test = ["pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist", "threadpoolctl"] -test-all = ["array-api-strict", "astropy[test]", "coverage[toml]", "ipython (>=4.2)", "objgraph", "sgp4 (>=2.3)", "skyfield (>=1.20)"] -typing = ["typing_extensions (>=4.0.0)"] +all = ["asdf-astropy (>=0.3)", "astropy[ipython]", "astropy[jupyter]", "astropy[recommended]", "beautifulsoup4 (>=4.9.3)", "bleach (>=3.2.1)", "bottleneck (>=1.3.3)", "certifi (>=2022.6.15.1)", "dask[dataframe] (>=2024.8.0)", "fsspec[http] (>=2023.4.0)", "h5py (>=3.9.0)", "html5lib (>=1.1)", "jplephem (>=2.17.0)", "mpmath (>=1.2.1)", "pyarrow (>=14.0.2)", "pytz (>=2016.10)", "s3fs (>=2023.4.0)", "sortedcontainers (>=2.1.0)", "uncompresspy (>=0.4.0)"] +dev = ["astropy[docs]", "astropy[recommended]", "astropy[test]", "astropy[typing]"] +dev-all = ["astropy[dev]", "astropy[test-all]", "tox (>=4.22.0)"] +docs = ["Jinja2 (>=3.1.3)", "astropy[recommended]", "matplotlib (!=3.9.0)", "pytest (>=8.0.0)", "sphinx (>=8.2.0,<9)", "sphinx-astropy[confv2] (>=1.9.1)", "sphinx-changelog (>=1.2.0)", "sphinx_design (>=0.6.1)", "sphinxcontrib-globalsubs (>=0.1.1)"] +ipython = ["ipython (>=8.0.0)"] +jupyter = ["astropy[ipython]", "ipydatagrid (>=1.1.13)", "ipykernel (>=6.16.0)", "ipywidgets (>=7.7.3)", "jupyter-core (>=4.11.2)", "pandas (>=2.0.0)"] +recommended = ["matplotlib (>=3.8.0)", "narwhals (>=1.42.0)", "pyparsing (>=2.4.0)", "scipy (>=1.9.2)"] +test = ["coverage (>=6.4.4)", "pre-commit (>=2.9.3)", "pytest (>=8.0.0)", "pytest-astropy (>=0.10.0)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=1.4.0)", "pytest-xdist (>=2.5.0)", "threadpoolctl (>=3.0.0)"] +test-all = ["array-api-strict (<2.4) ; python_version < \"3.12\"", "array-api-strict (>=1.0)", "astropy[all]", "astropy[test]", "numpy-quaddtype (>=0.2.2)", "objgraph (>=3.1.2)", "sgp4 (>=2.3)", "skyfield (>=1.42.0)"] +typing = ["narwhals (>=1.42.0)", "pandas-stubs (>=2.0.0)"] [[package]] name = "astropy-iers-data" -version = "0.2026.6.15.15.33.16" +version = "0.2026.7.6.1.1.20" description = "IERS Earth Rotation and Leap Second tables for the astropy core package" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "astropy_iers_data-0.2026.6.15.15.33.16-py3-none-any.whl", hash = "sha256:5658b05ccf5a0a9a593e719a90a4f09a21e49e2fe745aa38f3c0d49369a516ff"}, - {file = "astropy_iers_data-0.2026.6.15.15.33.16.tar.gz", hash = "sha256:efc9f2b308083434cf07157c85195ffa61fa74ea6c81e5c009588baa2176769b"}, + {file = "astropy_iers_data-0.2026.7.6.1.1.20-py3-none-any.whl", hash = "sha256:7aa50fa7f6d13258357d1e17fa97f3a8c2f04b9a7429d056e986a91cc46576a0"}, + {file = "astropy_iers_data-0.2026.7.6.1.1.20.tar.gz", hash = "sha256:bc38de235802c9f006c8a04693706e2adcc9e7ac1cbf415dc042b5f0ac14f1f3"}, ] [package.extras] @@ -283,9 +263,6 @@ files = [ {file = "async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - [[package]] name = "attrs" version = "23.1.0" @@ -322,18 +299,18 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "beautifulsoup4" -version = "4.13.3" +version = "4.15.0" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" groups = ["main", "dev", "docs"] files = [ - {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, - {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, + {file = "beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9"}, + {file = "beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7"}, ] [package.dependencies] -soupsieve = ">1.2" +soupsieve = ">=1.6.1" typing-extensions = ">=4.0.0" [package.extras] @@ -799,9 +776,6 @@ files = [ {file = "coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] @@ -875,35 +849,16 @@ files = [ [[package]] name = "docutils" -version = "0.21.2" +version = "0.22.4" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" groups = ["main", "docs"] files = [ - {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, - {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev", "docs", "test"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, + {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"}, + {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "executing" version = "2.0.1" @@ -1026,7 +981,7 @@ description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" groups = ["docs"] -markers = "(platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") and python_version < \"3.13\"" +markers = "python_version == \"3.12\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")" files = [ {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, @@ -1349,7 +1304,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} @@ -1538,6 +1492,26 @@ jupyterlab = "*" nbconvert = "*" notebook = "*" +[[package]] +name = "jupyter-builder" +version = "1.2.2" +description = "JupyterLab build tools" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "jupyter_builder-1.2.2-py3-none-any.whl", hash = "sha256:6ebcd4c49daf5df6a18068a74a48010406700ed90a76c189fac43eaf85c60c63"}, + {file = "jupyter_builder-1.2.2.tar.gz", hash = "sha256:b6cea88f58e44b2c5eba96f28d2e0d16fd453d3ca6dc9c4492ff8a1f2e97f601"}, +] + +[package.dependencies] +jupyter-core = "*" +traitlets = "*" + +[package.extras] +dev = ["build", "hatch", "mypy", "pre-commit", "ruff (==0.16.0)"] +test = ["copier (>=9.3,<10)", "coverage[toml] (>=7.10.6)", "deptry", "jinja2-time", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-cov (>=7)"] + [[package]] name = "jupyter-cache" version = "1.0.0" @@ -1701,7 +1675,6 @@ jupyter-events = ">=0.11.0" jupyter-server-terminals = ">=0.4.4" nbconvert = ">=6.4.4" nbformat = ">=5.3.0" -overrides = {version = ">=5.0", markers = "python_version < \"3.12\""} packaging = ">=22.0" prometheus-client = ">=0.9" pywinpty = {version = ">=2.0.1,<3.0.4 || >3.0.4", markers = "os_name == \"nt\""} @@ -1762,14 +1735,14 @@ test = ["bash-kernel", "pytest"] [[package]] name = "jupyterlab" -version = "4.5.7" +version = "4.6.3" description = "JupyterLab computational environment" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "jupyterlab-4.5.7-py3-none-any.whl", hash = "sha256:fba4cb0e2c44a52859669d8c98b45de029d5e515f8407bf8534d2a8fc5f0964d"}, - {file = "jupyterlab-4.5.7.tar.gz", hash = "sha256:55a9822c4754da305f41e113452c68383e214dcf96de760146af89ce5d5117b0"}, + {file = "jupyterlab-4.6.3-py3-none-any.whl", hash = "sha256:0a1ebc6567186f1eabd99536e94df7ed9e96d1e7c5ddf3e4406ae16e88abacb7"}, + {file = "jupyterlab-4.6.3.tar.gz", hash = "sha256:2e3db6e3a12495ebd188276e985bf5ac502fbde3d1e8628819920210008de498"}, ] [package.dependencies] @@ -1777,22 +1750,20 @@ async-lru = ">=1.0.0" httpx = ">=0.25.0,<1" ipykernel = ">=6.5.0,<6.30.0 || >6.30.0" jinja2 = ">=3.0.3" +jupyter-builder = ">=1.0.2" jupyter-core = "*" jupyter-lsp = ">=2.0.0" -jupyter-server = ">=2.4.0,<3" +jupyter-server = ">=2.19.0,<3" jupyterlab-server = ">=2.28.0,<3" notebook-shim = ">=0.2" -packaging = "*" -setuptools = ">=41.1.0" -tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} +packaging = ">=23.2" tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.11.12)"] -docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<8.2.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==6.0.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.3.post1)", "matplotlib (==3.10.0)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.15.1)"] -test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.15.15)", "shellcheck-py"] +docs-screenshots = ["altair (==6.0.0)", "ipykernel (<7.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.3.post1)", "matplotlib (==3.10.0)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.15.1)"] +test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "pytest-xdist", "requests", "requests-cache", "virtualenv"] upgrade-extension = ["copier (>=9,<10)", "jinja2-time (<0.3)", "pydantic (<3.0)", "pyyaml-include (<3.0)", "tomli-w (<2.0)"] [[package]] @@ -1847,14 +1818,14 @@ files = [ [[package]] name = "jupytext" -version = "1.19.3" +version = "1.19.5" description = "Jupyter notebooks as Markdown documents, Julia, Python or R scripts" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "jupytext-1.19.3-py3-none-any.whl", hash = "sha256:acf75492f80895ad8e664fd8db1708b617008dd0e71c341a1abc3d0d07310ed0"}, - {file = "jupytext-1.19.3.tar.gz", hash = "sha256:713c3ed4441afe0f31474d28ea2e6b61a268c04c40fd78e5ccfd7f7ac9e9f766"}, + {file = "jupytext-1.19.5-py3-none-any.whl", hash = "sha256:af22351202171116b986fe1f4f9233a13ca4a85d5eec57a87900ed48521dbae4"}, + {file = "jupytext-1.19.5.tar.gz", hash = "sha256:605026446d605aa54fd7f7fc69df6ae51c7a46053d4cebf05afdc64d66de3df0"}, ] [package.dependencies] @@ -1863,7 +1834,6 @@ mdit-py-plugins = "*" nbformat = "*" packaging = "*" pyyaml = "*" -tomli = {version = "*", markers = "python_version < \"3.11\""} [package.extras] dev = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs[fs] (>=1.0)", "jupyter-server (!=2.11)", "marimo (>=0.17.6,<=0.19.4)", "nbconvert", "pre-commit", "pytest", "pytest-asyncio", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist", "sphinx", "sphinx-gallery (>=0.8)"] @@ -2004,146 +1974,146 @@ files = [ [[package]] name = "lxml" -version = "6.1.0" +version = "6.1.1" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "lxml-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41dcc4c7b10484257cbd6c37b83ddb26df2b0e5aff5ac00d095689015af868ec"}, - {file = "lxml-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a31286dbb5e74c8e9a5344465b77ab4c5bd511a253b355b5ca2fae7e579fafec"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1bc4cc83fb7f66ffb16f74d6dd0162e144333fc36ebcce32246f80c8735b2551"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:20cf4d0651987c906a2f5cba4e3a8d6ba4bfdf973cfe2a96c0d6053888ea2ecd"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb34ea45a82dd637c2c97ae1bbb920850c1e59bcae79ce1c15af531d83e7215"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1d9b99e5b2597e4f5aed2484fef835256fa1b68a19e4265c97628ef4bf8bcf4"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:d43aa26dcda363f21e79afa0668f5029ed7394b3bb8c92a6927a3d34e8b610ea"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:6262b87f9e5c1e5fe501d6c153247289af42eb44ad7660b9b3de17baaf92d6f6"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1392c569c032f78a11a25d1de1c43fff13294c793b39e19d84fade3045cbbc3"}, - {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:045e387d1f4f42a418380930fa3f45c73c9b392faf67e495e58902e68e8f44a7"}, - {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9f93d5b8b07f73e8c77e3c6556a3db269918390c804b5e5fcdd4858232cc8f16"}, - {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:de550d129f18d8ab819651ffe4f38b1b713c7e116707de3c0c6400d0ef34fbc1"}, - {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c08da09dc003c9e8c70e06b53a11db6fb3b250c21c4236b03c7d7b443c318e7a"}, - {file = "lxml-6.1.0-cp310-cp310-win32.whl", hash = "sha256:37448bf9c7d7adfc5254763901e2bbd6bb876228dfc1fc7f66e58c06368a7544"}, - {file = "lxml-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:2593a0a6621545b9095b71ad74ed4226eba438a7d9fc3712a99bdb15508cf93a"}, - {file = "lxml-6.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80807d72f96b96ad5588cb85c75616e4f2795a7737d4630784c51497beb7776"}, - {file = "lxml-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cec05be8c876f92a5aa07b01d60bbb4d11cfbdd654cad0561c0d7b5c043a61b9"}, - {file = "lxml-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9c03e048b6ce8e77b09c734e931584894ecd58d08296804ca2d0b184c933ce50"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:942454ff253da14218f972b23dc72fa4edf6c943f37edd19cd697618b626fac5"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d036ee7b99d5148072ac7c9b847193decdfeac633db350363f7bce4fff108f0e"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ae5d8d5427f3cc317e7950f2da7ad276df0cfa37b8de2f5658959e618ea8512"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:363e47283bde87051b821826e71dde47f107e08614e1aa312ba0c5711e77738c"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:f504d861d9f2a8f94020130adac88d66de93841707a23a86244263d1e54682f5"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:23a5dc68e08ed13331d61815c08f260f46b4a60fdd1640bbeb82cf89a9d90289"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f15401d8d3dbf239e23c818afc10c7207f7b95f9a307e092122b6f86dd43209a"}, - {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fcf3da95e93349e0647d48d4b36a12783105bcc74cb0c416952f9988410846a3"}, - {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0d082495c5fcf426e425a6e28daaba1fcb6d8f854a4ff01effb1f1f381203eb9"}, - {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e3c4f84b24a1fcba435157d111c4b755099c6ff00a3daee1ad281817de75ed11"}, - {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:976a6b39b1b13e8c354ad8d3f261f3a4ac6609518af91bdb5094760a08f132c4"}, - {file = "lxml-6.1.0-cp311-cp311-win32.whl", hash = "sha256:857efde87d365706590847b916baff69c0bc9252dc5af030e378c9800c0b10e3"}, - {file = "lxml-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:183bfb45a493081943be7ea2b5adfc2b611e1cf377cefa8b8a8be404f45ef9a7"}, - {file = "lxml-6.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:19f4164243fc206d12ed3d866e80e74f5bc3627966520da1a5f97e42c32a3f39"}, - {file = "lxml-6.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d2f17a16cd8751e8eb233a7e41aecdf8e511712e00088bf9be455f604cd0d28d"}, - {file = "lxml-6.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0cea5b1d3e6e77d71bd2b9972eb2446221a69dc52bb0b9c3c6f6e5700592d93"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc46da94826188ed45cb53bd8e3fc076ae22675aea2087843d4735627f867c6d"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9147d8e386ec3b82c3b15d88927f734f565b0aaadef7def562b853adca45784a"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5715e0e28736a070f3f34a7ccc09e2fdcba0e3060abbcf61a1a5718ff6d6b105"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4937460dc5df0cdd2f06a86c285c28afda06aefa3af949f9477d3e8df430c485"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc783ee3147e60a25aa0445ea82b3e8aabb83b240f2b95d32cb75587ff781814"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:40d9189f80075f2e1f88db21ef815a2b17b28adf8e50aaf5c789bfe737027f32"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:05b9b8787e35bec69e68daf4952b2e6dfcfb0db7ecf1a06f8cdfbbac4eb71aad"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0f08beb0182e3e9a86fae124b3c47a7b41b7b69b225e1377db983802404e54"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73becf6d8c81d4c76b1014dbd3584cb26d904492dcf73ca85dc8bff08dcd6d2d"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1ae225f66e5938f4fa29d37e009a3bb3b13032ac57eb4eb42afa44f6e4054e69"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:690022c7fae793b0489aa68a658822cea83e0d5933781811cabbf5ea3bcfe73d"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:63aeafc26aac0be8aff14af7871249e87ea1319be92090bfd632ec68e03b16a5"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:264c605ab9c0e4aa1a679636f4582c4d3313700009fac3ec9c3412ed0d8f3e1d"}, - {file = "lxml-6.1.0-cp312-cp312-win32.whl", hash = "sha256:56971379bc5ee8037c5a0f09fa88f66cdb7d37c3e38af3e45cf539f41131ac1f"}, - {file = "lxml-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bba078de0031c219e5dd06cf3e6bf8fb8e6e64a77819b358f53bb132e3e03366"}, - {file = "lxml-6.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:c3592631e652afa34999a088f98ba7dfc7d6aff0d535c410bea77a71743f3819"}, - {file = "lxml-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a0092f2b107b69601adf562a57c956fbb596e05e3e6651cabd3054113b007e45"}, - {file = "lxml-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc7140d7a7386e6b545d41b7358f4d02b656d4053f5fa6859f92f4b9c2572c4d"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:419c58fc92cc3a2c3fa5f78c63dbf5da70c1fa9c1b25f25727ecee89a96c7de2"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:37fabd1452852636cf38ecdcc9dd5ca4bba7a35d6c53fa09725deeb894a87491"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2853c8b2170cc6cd54a6b4d50d2c1a8a7aeca201f23804b4898525c7a152cfc"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e369cbd690e788c8d15e56222d91a09c6a417f49cbc543040cba0fe2e25a79e"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e69aa6805905807186eb00e66c6d97a935c928275182eb02ee40ba00da9623b2"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:4bd1bdb8a9e0e2dd229de19b5f8aebac80e916921b4b2c6ef8a52bc131d0c1f9"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:cbd7b79cdcb4986ad78a2662625882747f09db5e4cd7b2ae178a88c9c51b3dfe"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:43e4d297f11080ec9d64a4b1ad7ac02b4484c9f0e2179d9c4ef78e886e747b88"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cc16682cc987a3da00aa56a3aa3075b08edb10d9b1e476938cfdbee8f3b67181"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d8efe71429635f0559579092bb5e60560d7b9115ee38c4adbea35632e7fa24"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e39ab3a28af7784e206d8606ec0e4bcad0190f63a492bca95e94e5a4aef7f6e"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9eb667bf50856c4a58145f8ca2d5e5be160191e79eb9e30855a476191b3c3495"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7f4a77d6f7edf9230cee3e1f7f6764722a41604ee5681844f18db9a81ea0ec33"}, - {file = "lxml-6.1.0-cp313-cp313-win32.whl", hash = "sha256:28902146ffbe5222df411c5d19e5352490122e14447e98cd118907ee3fd6ee62"}, - {file = "lxml-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:4a1503c56e4e2b38dc76f2f2da7bae69670c0f1933e27cfa34b2fa5876410b16"}, - {file = "lxml-6.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:e0af85773850417d994d019741239b901b22c6680206f46a34766926e466141d"}, - {file = "lxml-6.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:ab863fd37458fed6456525f297d21239d987800c46e67da5ef04fc6b3dd93ac8"}, - {file = "lxml-6.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6fd8b1df8254ff4fd93fd31da1fc15770bde23ac045be9bb1f87425702f61cc9"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:47024feaae386a92a146af0d2aeed65229bf6fff738e6a11dda6b0015fb8fd03"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3f00972f84450204cd5d93a5395965e348956aaceaadec693a22ec743f8ae3eb"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97faa0860e13b05b15a51fb4986421ef7a30f0b3334061c416e0981e9450ca4c"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:972a6451204798675407beaad97b868d0c733d9a74dafefc63120b81b8c2de28"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fe022f20bc4569ec66b63b3fb275a3d628d9d32da6326b2982584104db6d3086"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:75c4c7c619a744f972f4451bf5adf6d0fb00992a1ffc9fd78e13b0bc817cc99f"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3648f20d25102a22b6061c688beb3a805099ea4beb0a01ce62975d926944d292"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77b9f99b17cbf14026d1e618035077060fc7195dd940d025149f3e2e830fbfcb"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32662519149fd7a9db354175aa5e417d83485a8039b8aaa62f873ceee7ea4cad"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:73d658216fc173cf2c939e90e07b941c5e12736b0bf6a99e7af95459cfe8eabb"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ac4db068889f8772a4a698c5980ec302771bb545e10c4b095d4c8be26749616f"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:45e9dfbd1b661eb64ba0d4dbe762bd210c42d86dd1e5bd2bdf89d634231beb43"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89e8d73d09ac696a5ba42ec69787913d53284f12092f651506779314f10ba585"}, - {file = "lxml-6.1.0-cp314-cp314-win32.whl", hash = "sha256:ebe33f4ec1b2de38ceb225a1749a2965855bffeef435ba93cd2d5d540783bf2f"}, - {file = "lxml-6.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:398443df51c538bd578529aa7e5f7afc6c292644174b47961f3bf87fe5741120"}, - {file = "lxml-6.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:8c8984e1d8c4b3949e419158fda14d921ff703a9ed8a47236c6eb7a2b6cb4946"}, - {file = "lxml-6.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1081dd10bc6fa437db2500e13993abf7cc30716d0a2f40e65abb935f02ec559c"}, - {file = "lxml-6.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:dabecc48db5f42ba348d1f5d5afdc54c6c4cc758e676926c7cd327045749517d"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e3dd5fe19c9e0ac818a9c7f132a5e43c1339ec1cbbfecb1a938bd3a47875b7c9"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e7b0a4ca6dcc007a4cef00a761bba2dea959de4bd2df98f926b33c92ca5dfb9"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d27bbe326c6b539c64b42638b18bc6003a8d88f76213a97ac9ed4f885efeab7"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4e425db0c5445ef0ad56b0eec54f89b88b2d884656e536a90b2f52aecb4ca86"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b89b098105b8599dc57adac95d1813409ac476d3c948a498775d3d0c6124bfb"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:c4a699432846df86cc3de502ee85f445ebad748a1c6021d445f3e514d2cd4b1c"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:30e7b2ed63b6c8e97cca8af048589a788ab5c9c905f36d9cf1c2bb549f450d2f"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:022981127642fe19866d2907d76241bb07ed21749601f727d5d5dd1ce5d1b773"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:23cad0cc86046d4222f7f418910e46b89971c5a45d3c8abfad0f64b7b05e4a9b"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:21c3302068f50d1e8728c67c87ba92aa87043abee517aa2576cca1855326b405"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:be10838781cb3be19251e276910cd508fe127e27c3242e50521521a0f3781690"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2173a7bffe97667bbf0767f8a99e587740a8c56fdf3befac4b09cb29a80276fd"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c6854e9cf99c84beb004eecd7d3a3868ef1109bf2b1df92d7bc11e96a36c2180"}, - {file = "lxml-6.1.0-cp314-cp314t-win32.whl", hash = "sha256:00750d63ef0031a05331b9223463b1c7c02b9004cef2346a5b2877f0f9494dd2"}, - {file = "lxml-6.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:80410c3a7e3c617af04de17caa9f9f20adaa817093293d69eae7d7d0522836f5"}, - {file = "lxml-6.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:26dd9f57ee3bd41e7d35b4c98a2ffd89ed11591649f421f0ec19f67d50ec67ac"}, - {file = "lxml-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b6c2f225662bc5ad416bdd06f72ca301b31b39ce4261f0e0097017fc2891b940"}, - {file = "lxml-6.1.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a86f06f059e22a0d574990ee2df24ede03f7f3c68c1336293eee9536c4c776cd"}, - {file = "lxml-6.1.0-cp38-cp38-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:468479e52ecf3ec23799c863336d02c05fc2f7ffd1a1424eeeb9a28d4eb69d13"}, - {file = "lxml-6.1.0-cp38-cp38-manylinux_2_28_i686.whl", hash = "sha256:a02ca8fe48815bddcfca3248efe54451abb9dbf2f7d1c5744c8aa4142d476919"}, - {file = "lxml-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bb40648d96157f9081886defe13eac99253e663be969ff938a9289eff6e47b72"}, - {file = "lxml-6.1.0-cp38-cp38-win32.whl", hash = "sha256:1dd6a1c3ad4cb674f44525d9957f3e9c209bb6dd9213245195167a281fcc2bdc"}, - {file = "lxml-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:4e2c54d6b47361d0f1d3bc8d4e082ad87201e56ccdcca4d3b9ee3644ff595ec8"}, - {file = "lxml-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:920354904d1cb86577d4b3cfe2830c2dbe81d6f4449e57ada428f1609b5985f7"}, - {file = "lxml-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c871299c595ee004d186f61840f0bfc4941aa3f17c8ba4a565ead7e4f4f820ee"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d0d799ff958655781296ec870d5e2448e75150da2b3d07f13ff5b0c2c35beefd"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ba11752e346bd804ea312ec2eea2532dfa8b8d3261d81a32ef9e6ab16256280"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26c5272c6a4bf4cf32d3f5a7890c942b0e04438691157d341616d02cca74d4bd"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c53fa3a5a52122d590e847a57ccf955557b9634a7f99ff5a35131321b0a85317"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_28_i686.whl", hash = "sha256:76b958b4ea3104483c20f74866d55aa056546e15ebe83dd7aecd63698f43b755"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:8c11b984b5ce6add4dccc7144c7be5d364d298f15b0c6a57da1991baedc750ce"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d3829a6e6fd550a219564912d4002c537f65da4c6ae4e093cc34462f4fa027ad"}, - {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:52b0ac6903cf74ebf997eb8c682d2fbac7d1ab7e4c552413eec55868a9b73f39"}, - {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:29f5c00cb7d752bce2c70ebd2d31b0a42f9499ffdd3ecb2f31a5b73ee43031ad"}, - {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:c748ebcb6877de89f48ab90ca96642ac458fff5dec291a2b9337cd4d0934e383"}, - {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:08950a23f296b3f83521577274e3d3b0f3d739bf2e68d01a752e4288bc50d286"}, - {file = "lxml-6.1.0-cp39-cp39-win32.whl", hash = "sha256:11a873c77a181b4fef9c2e357d08ed399542c2af1390101da66720a19c7c9618"}, - {file = "lxml-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:81ff55c70b67d19d52b6fd118a114c0a4c97d799cd3089ff9bd9e2ff4b414ee2"}, - {file = "lxml-6.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:481d6e2104285d9add34f41b42b247b76b61c5b5c26c303c2e9707bbf8bd9a64"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:546b66c0dd1bb8d9fa89d7123e5fa19a8aff3a1f2141eb22df96112afb17b842"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfa1a34df366d9dc0d5eaf420f4cf2bb1e1bebe1066d1c2fc28c179f8a4004c"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db88156fcf544cdbf0d95588051515cfdfd4c876fc66444eb98bceb5d6db76de"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07f98f5496f96bf724b1e3c933c107f0cbf2745db18c03d2e13a291c3afd2635"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4642e04449a1e164b5ff71ffd901ddb772dfabf5c9adf1b7be5dffe1212bc037"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7da13bb6fbadfafb474e0226a30570a3445cfd47c86296f2446dafbd77079ace"}, - {file = "lxml-6.1.0.tar.gz", hash = "sha256:bfd57d8008c4965709a919c3e9a98f76c2c7cb319086b3d26858250620023b13"}, + {file = "lxml-6.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:09dd5b7075dc2f7709654a46543ba1ea3c2e217b2ed8fbd413a8a945a0f40f60"}, + {file = "lxml-6.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f6ac4ef4d82dff54670227a69c67782ae0b811b5cf6b17954f1e8f7502fc0d1d"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:556e94a63c9b04716f8e4de2abb65775061f846e89331b6c5be79183a24f98ea"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c6bf403fbb3b3e348a561a5f4f0b9961835657981c802a1df03653eef8a9074"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1dde6131244bba38a17c745836ba190bc753fd73c9291666287fd0a3fa3dcf30"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98fc784c2c1440667aeedf8465bdfe10208acf0ead656a2c68627299f546b315"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:add8cf6ddf9a65116119a28ece0f7886e30af27ba724a7594305f1d1b58a92a1"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:cf9d57306d848218f3601fee7601fab1a327c942d56e2e97610583cb4dd74206"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88136950da4d13c318bde414ce10219931937851327f44328f2df4d2c4614067"}, + {file = "lxml-6.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cecdd5dfdc87b1fd87dbf81d4b037a544f47f4c744200a67013771682d67686a"}, + {file = "lxml-6.1.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd312b9692e831d2ffcad61eab31d91d4b4655a962e61de8fb410472cbcd37aa"}, + {file = "lxml-6.1.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5b7328b46d49fc9477d91ae8f6d55340347d827b7734ba3ea33faae0efef1383"}, + {file = "lxml-6.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37a58976370f36d9329d118ad0b953c5aeb9119ac9c6a4e258942a225d0573a1"}, + {file = "lxml-6.1.1-cp310-cp310-win32.whl", hash = "sha256:cea3f4c1af79af13cdb2da0c028111d8f8522d4f22a000c82385535f24e5cf3a"}, + {file = "lxml-6.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:3abf332af33a74288675d936fe861fd4344da0dd6622193fbc4f2bfbb35536b5"}, + {file = "lxml-6.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:8dadbe5b217ff35b6a8d16610dd710219b59b76d13f0e3f0d9f36786206e4485"}, + {file = "lxml-6.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:53b7d2b7a10b1c35c0a5e21e9224accf60c1bbfba523990732e521b2b73adef2"}, + {file = "lxml-6.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3f333630ab480244a1bff72043e511a91eb22e7595dead8653ee5612dd8f3d"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a4bbea04c97f6d78a48e3fbc1cb9116d2780b1b39e03a23f6eb9b603fd61f510"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db1d75f6617a49c1c01bc7023713e0ff59ab32c9579ae62a7674c0e34f3b0b0a"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a12689be69a28ddaa0ab99a5a1137da2afd5f8f16df7b5680b66f616d3eda1d"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b73c339ae29b90fd2d06e58ebd555a751bde9cd6bbd36cc0281b9a2c94e9d8"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:752d3bbfe874715ccd0aec7f88d7fc623c0f1fd7aa7b3238a084e017bad2a009"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:6b1761fbf9ec984e2e9d9c589ef5f5fd684b7c19f92aadd567a26c5224958db6"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d680fbcb768404c601ecb43519ecd8461f6954cb11c06a78962f666832ccfca8"}, + {file = "lxml-6.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:162af1091cd785f2f27e62d3547ae9bc58ec5c86dd314d67021fd02463708d83"}, + {file = "lxml-6.1.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e9308ff8241c532df3f3e570f9a5aeed6c853f888512ba4b75638d7c11c95ef6"}, + {file = "lxml-6.1.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f6994074ebae6ffb04447268e37dc16edc304f9859cf91acb86e0af6c1b395c"}, + {file = "lxml-6.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:80c2dfadb855da477cf73373ad29a333535dedb9b12bad02c9814c8e2b43bf08"}, + {file = "lxml-6.1.1-cp311-cp311-win32.whl", hash = "sha256:30a89d3ac8faec007453fb541f3f46807eeec88edd5826f6e3fe001752a2c621"}, + {file = "lxml-6.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:abbefa31eee84842140f67acef1c828e28bba8bbf0c3bc6e5492a9af88152c28"}, + {file = "lxml-6.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:dcb292aa7fe485ceff7af4f92e46c5af397daec5dff64871a528f0fc47a3cc5b"}, + {file = "lxml-6.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:104c09bda8d2a562824c0e319d0768ce26a779b7601e0931d33b09b53c392ef7"}, + {file = "lxml-6.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25c6997a9a534e016695a0ba06b2f07945de682731ff01065b6d5a4474179da1"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c921ba5c51e4e9f63b8b00267d06566e1f63407408a0496da2d1d0bfc819c7fc"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:54a7f95e4de5fb94e2f9f4b9055c6ba33bf3d628fd77a1d647c5923caa2cdcdc"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f2ec43df44b1f76249ee0a615334f9b5b060e1c8bd90e706dad2d14d02f383"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70ef8a7e102a1508f8121aae5b0867abd663f72c14f0a9c937e6554cb4587b7b"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:27acc820660aaffa4f7c087f29120e12980f7779d56d8492d263170111284740"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:1db753c9115ec7100d073b744d17e25e88a8f90f5c39b2f5dd878149af59671f"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4f469aebd783bb741c2ecb2a681008fd26bfe5c16a9a72ed5467f834e810df2"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:766b010012d59470072c1816b5b6c69f1d243e5db36ea5968e94accf430a4635"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b8d812c6011c08b8111a15e54dd990b8923692d80adf35488bee34026c35accf"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe0306bd29505a9177aac19f1877174b0e7422c222a59f70b2cd41633448c3dc"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5ba186ad207446c65d3bb3d3e0412b032b1d9f595e59861e2354798c5703d955"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa366a1e55b8ebfe8ca8ddc3cfe75c8ebade181aeb0f661d0cb05986b647f72a"}, + {file = "lxml-6.1.1-cp312-cp312-win32.whl", hash = "sha256:126c93f7f56f0eda92f6d8c619edc463a4f23d9252f1c9d0405a76f25fa9f11a"}, + {file = "lxml-6.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:26e6eda8d38c1fcab1090dd196ee87cbd13788e531937610e2589085de074e77"}, + {file = "lxml-6.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:6540377fbd53fe1b629172288c464fb18db11ce1fa7dc15891da10aa9dcc3e7f"}, + {file = "lxml-6.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736"}, + {file = "lxml-6.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947"}, + {file = "lxml-6.1.1-cp313-cp313-win32.whl", hash = "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca"}, + {file = "lxml-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660"}, + {file = "lxml-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc"}, + {file = "lxml-6.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0"}, + {file = "lxml-6.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb"}, + {file = "lxml-6.1.1-cp314-cp314-win32.whl", hash = "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603"}, + {file = "lxml-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137"}, + {file = "lxml-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf"}, + {file = "lxml-6.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee"}, + {file = "lxml-6.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e"}, + {file = "lxml-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1"}, + {file = "lxml-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e"}, + {file = "lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c"}, + {file = "lxml-6.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6689e828a94eee4f139408c337bb198e014724bb8a8c26d3cfac49d119ed69a6"}, + {file = "lxml-6.1.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bdebcc8a75d38c7598dfb2c9ed852d7a9eb4a10d6e2d0764b919b802bf32ac88"}, + {file = "lxml-6.1.1-cp38-cp38-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8be8ad51249698103d24b0571df35a10990fbe93dd043b6c024172189485f5e3"}, + {file = "lxml-6.1.1-cp38-cp38-manylinux_2_28_i686.whl", hash = "sha256:76447f65250ed2501ead1a1552f5ce8edff159a86f308348e6a9c4acb5e1f1b4"}, + {file = "lxml-6.1.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ffecec8eb889b58ba9be5b95fb1cc78e22ea8eedea38e8736a1568fe1979250e"}, + {file = "lxml-6.1.1-cp38-cp38-win32.whl", hash = "sha256:c674693f055fa2495de12292cb45e9944199d8eaef5a2dec45175c7c61cb73e3"}, + {file = "lxml-6.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:55b03549819867ea141c0202242c4816c82e52ec36e7e648db9d8da5a3dc3ed6"}, + {file = "lxml-6.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9f79d5325907f13e1be0b3e4dacc1049d1dffc4aeee3c995284bea5fe0fab7d"}, + {file = "lxml-6.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:83b6b30eb131da7a75b601f28c5d6971e6ed3e887919bf6b6a1ad3c2df289080"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:441dd227fa0690eb9fc81edabc63cdcefc212bba99b906dcf6e32cc1a9d3e533"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e07c65f443c887bbcf31cc1771d932ecc192a5273943589b3c7572b749f1ffb2"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5bec7d03d78d853597d6107854c2310ce3f761fd218fe9fe91d5101fcf6c2efe"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f76acfb5f68ba982635a53fd985a8044be98a35b43232c2a1ee235ffab3e1dd"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_28_i686.whl", hash = "sha256:8d43ca737b20e106e4aebc42b2f3ae19f00ba63d7eb731698ee083d72d15646f"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:32ab449a5486f6c758e849bb86710d0e45edc24a04e250c01555f8f5653958f8"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53c909b62a0532183542fed00c5a7218258c56292d409bc789886fe1cb04c438"}, + {file = "lxml-6.1.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:640f97d43d867bcb9c75b3af013b64850756b746cb6bce8ace83b70da3abba9d"}, + {file = "lxml-6.1.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:469e3618338bd7ab5beb412d2439825479fcf0dab99e394ca563dbc4eaf6c834"}, + {file = "lxml-6.1.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:aae97dfdb60715c164419ac2532a76d013c3918a665eb6cb7288098b5f349aaf"}, + {file = "lxml-6.1.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c9a4b821dc7055bf9e05ff5719e18ec501f75c0f0bbfabd573b277559780833d"}, + {file = "lxml-6.1.1-cp39-cp39-win32.whl", hash = "sha256:639f6c857d91d9be29bd7502348d6736dab168b54b5158cd899abf11684dc186"}, + {file = "lxml-6.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:34c2d737beabfe35baada43941ed519251e9a12e779031496bcd5d539fcfd730"}, + {file = "lxml-6.1.1-cp39-cp39-win_arm64.whl", hash = "sha256:07a4a68e286ee7a1ed7dfb8af83e615757c0ccfe9f18c6b4ea6771388d9ba8c9"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:31033dc34636ea6b7d5cc11b1ddbda78a14de858ba9d3e1ed4b69a3085bc521e"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3893c14c4b6ac5b2d54ba8cf03e99fe5104e592de491f19bd6b82756c09f8004"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c07da4cebf6889f03ebac8d238f62318e29f495de0aa18a51ea14e61ae907e2e"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6f0ce10945fab9c4c06ce14e22af9059d1a87493a9af4501a5b0b9187e21cf2"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f8844cd288697c6425c9beba919302241e3278871dc6519515e72b04e987abcf"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ed21202aec73cda4d55d1ce57b389aadb90ffb044e6cd1080b8347efe1b1ec84"}, + {file = "lxml-6.1.1.tar.gz", hash = "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40"}, ] [package.extras] @@ -2154,14 +2124,14 @@ htmlsoup = ["BeautifulSoup4"] [[package]] name = "markdown-it-py" -version = "3.0.0" +version = "4.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev", "docs"] files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, + {file = "markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a"}, + {file = "markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49"}, ] [package.dependencies] @@ -2169,13 +2139,12 @@ mdurl = ">=0.1,<1.0" [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] +plugins = ["mdit-py-plugins (>=0.5.0)"] profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "pytest-timeout", "requests"] [[package]] name = "markupsafe" @@ -2249,83 +2218,71 @@ files = [ [[package]] name = "matplotlib" -version = "3.10.8" +version = "3.11.1" description = "Python plotting package" optional = false -python-versions = ">=3.10" +python-versions = ">=3.11" groups = ["main", "dev", "docs"] files = [ - {file = "matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7"}, - {file = "matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656"}, - {file = "matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df"}, - {file = "matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17"}, - {file = "matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933"}, - {file = "matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a"}, - {file = "matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160"}, - {file = "matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78"}, - {file = "matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4"}, - {file = "matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2"}, - {file = "matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6"}, - {file = "matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9"}, - {file = "matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2"}, - {file = "matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a"}, - {file = "matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58"}, - {file = "matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04"}, - {file = "matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f"}, - {file = "matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466"}, - {file = "matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf"}, - {file = "matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b"}, - {file = "matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6"}, - {file = "matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1"}, - {file = "matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486"}, - {file = "matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce"}, - {file = "matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6"}, - {file = "matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149"}, - {file = "matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645"}, - {file = "matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077"}, - {file = "matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22"}, - {file = "matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39"}, - {file = "matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565"}, - {file = "matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a"}, - {file = "matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958"}, - {file = "matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5"}, - {file = "matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f"}, - {file = "matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b"}, - {file = "matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d"}, - {file = "matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008"}, - {file = "matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c"}, - {file = "matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11"}, - {file = "matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8"}, - {file = "matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50"}, - {file = "matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908"}, - {file = "matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a"}, - {file = "matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1"}, - {file = "matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c"}, - {file = "matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b"}, - {file = "matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f"}, - {file = "matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8"}, - {file = "matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7"}, - {file = "matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3"}, - {file = "matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1"}, - {file = "matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a"}, - {file = "matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2"}, - {file = "matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3"}, + {file = "matplotlib-3.11.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b7cf158e7add54a8d51ac9b5a84abd6d4e13ed4951b4f25f1c5139f41c2addb2"}, + {file = "matplotlib-3.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d2ace7273b9a5061a3b420918a16fae1f2dc5dfee1abcc13aba71b5d94b1820c"}, + {file = "matplotlib-3.11.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee55e9041211bf84302ab55ec3965df18dd90ae19f8b58332a7feaf208bfe83"}, + {file = "matplotlib-3.11.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f4bdeea33a8d15a071dbfe6d119451b1d719c733ac666d65357082901a9099"}, + {file = "matplotlib-3.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b4c78ceb2f11bcac7389d305cda17aeb1f4586a857854ab5780bd3dd8dbfc407"}, + {file = "matplotlib-3.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:7f33a781e12b1e53b278deb2f5373c2e55ec4f10727be3440c0cfb5cda9f944f"}, + {file = "matplotlib-3.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:67e4c3cd578c65ebd81bdc09a1b6592ceafee6dfafe116dc85dfcb647b5bbb18"}, + {file = "matplotlib-3.11.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e15ef41507f3d525f46154ac9e3ae785dacde9f20e593a25de8986267892ef74"}, + {file = "matplotlib-3.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:21a67b961a6d597bca54fae826cd20695ba4a6e4d05424a08da6e13e3176fd6b"}, + {file = "matplotlib-3.11.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba8f811b8ddfac493734d6af0b2dff96919d0c28ca0d641858dab4262777c6ea"}, + {file = "matplotlib-3.11.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c52f7ad20ef476806ed212380b1d54d20310c8b86bdc2c9a68b51f0024a44472"}, + {file = "matplotlib-3.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8b14eb22961fe865efb0e4ff167e333e428908b00115a8d800ccb65ee108e481"}, + {file = "matplotlib-3.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:88a2a27dd9691ae448dfae4b26f59036be90c3c28757edd3553a29559d00859f"}, + {file = "matplotlib-3.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:480194afceca4df2f137c2721227d3cba67121fbf4397b69cee7f83714b0a58a"}, + {file = "matplotlib-3.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6771b0cd7838c6a857a7209814158c0ad09bfef878db3033dd82d70ad101f191"}, + {file = "matplotlib-3.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2abdee5ffa2fe11b2d19f7a5c63b785fb7c28cc46c7bc1814156341d9d1a33e1"}, + {file = "matplotlib-3.11.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0a19dcf73406d3746d25a5ed42d713604c9a3e024d129b102852b0d941cb9f3"}, + {file = "matplotlib-3.11.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7389b77ed2ab0552f46d9a90b81b7b8e6dfcdc42adc36c37a0865799843e0e3e"}, + {file = "matplotlib-3.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c90be0b73568da4f662afac580956a76e308437e641b4a45aa08925eeb67d95f"}, + {file = "matplotlib-3.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:68408341f2312836fbbdf6b3c78047f65b2d8752f5fd221c3e72d348f5b34f8b"}, + {file = "matplotlib-3.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c1f44890d435c1b4ef52f701ad5828cb450ea97bcc83918fda6be74965d6cd2"}, + {file = "matplotlib-3.11.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:5e510088c27a89d53580a752f959146893563e63c330e161d159b0fee652af6f"}, + {file = "matplotlib-3.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1524e2bdd48a93557aa47ddcfe9c225dfdd57d5a01a5c49128c20f0632980ee1"}, + {file = "matplotlib-3.11.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:11664c551345553db92e61cae6cf1376f138f8c47cafdf13b64b18f3e3e9e464"}, + {file = "matplotlib-3.11.1-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e1f8922ba31959cf6a9dfb51be64b7f7bc582801a3957dc0c2f3afcd3537adf"}, + {file = "matplotlib-3.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83235693abde86e5e0129998f80ee39fc7f58e6d56a88fafb28a9278833e9d5f"}, + {file = "matplotlib-3.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:9a076f4fc5cdc43fdf510f5981418d25c2db4973418d9f22d8bb3dc8045ada78"}, + {file = "matplotlib-3.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:216fbb93a74add02ddb4cb38ef5348f59ac00b3e84567eaf16598772d40e150a"}, + {file = "matplotlib-3.11.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:30c492d4ba9448595b6fd8708c6725963f8148e25c0d8842948da5b05f0ee8d3"}, + {file = "matplotlib-3.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ac104be2768ffdd8655db9e71b768cbb45f2b9aa7b450cf1595e8f65d3822319"}, + {file = "matplotlib-3.11.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be943cb68bc6660ead58c55b3aa6366cba2ef7feb06460fbcce32360376f19f"}, + {file = "matplotlib-3.11.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5af0dcda57d471440a7b5b623e70e0a61003518443d9098f211a96ecfbbc25be"}, + {file = "matplotlib-3.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3d3fd84082b1afbd9398466c81309e20045be20d48fe0fb18c43504d164cbbb2"}, + {file = "matplotlib-3.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:9601a1e90be21e4884c53b4f3dc3ee0544654946f9975258d691f1c2e2f119c6"}, + {file = "matplotlib-3.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:ae30c6109848ac0f9fa36c5d6270938487614c47ba31860bd5361266dabc5685"}, + {file = "matplotlib-3.11.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:dadfe80797174e2984aae3be0b77594a3c72d2c0a40fbd4a0de48d2728caf3ae"}, + {file = "matplotlib-3.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:89b193b255f4f6f7948dbcee3691f4f341ab05d9a8874a67b45ddb4182922eda"}, + {file = "matplotlib-3.11.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191163532cdefcb1571ca38a6d7e6474baccde64495783e6ba47aa07ec4b9bbb"}, + {file = "matplotlib-3.11.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fdf1c818ab05d0e74002091ddaf414478a3a449ec9d51c8976d45be7e3a01e2"}, + {file = "matplotlib-3.11.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b937b9dba5f5f6c1e31c47abe2186c865c0914fd18f2ce0dfc39c9adcef5951d"}, + {file = "matplotlib-3.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f2912f647f3fbe1ccf085f91e213936f9101bead81a5e670565b1f1b3712f4fb"}, + {file = "matplotlib-3.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:54d47b8ae8b579633a3902ca5b4ad6c1e132a5626d64447b2e22a66394e79987"}, + {file = "matplotlib-3.11.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:427258425f9a3fc4ed79a91f9e9b9aaf5a82cb6571e85dc14063cc6fbb993741"}, + {file = "matplotlib-3.11.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:1ac697e591c11b6ad04679a73c2d2f9980fe9d9f0311fb414a2e329706343dfb"}, + {file = "matplotlib-3.11.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e4b9ac2f1f607ecda2af90a5232beee2af7582fce1cc30c4b6a1b012dc21ee99"}, + {file = "matplotlib-3.11.1.tar.gz", hash = "sha256:69647db5746941c793d6e445a4cd349323ffb87d9cc958c2ad84a659b4832d30"}, ] [package.dependencies] contourpy = ">=1.0.1" cycler = ">=0.10" -fonttools = ">=4.22.0" +fonttools = ">=4.28.2" kiwisolver = ">=1.3.1" -numpy = ">=1.23" +numpy = ">=1.25" packaging = ">=20.0" -pillow = ">=8" +pillow = ">=9" pyparsing = ">=3" python-dateutil = ">=2.7" -[package.extras] -dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] - [[package]] name = "matplotlib-inline" version = "0.1.6" @@ -2343,23 +2300,23 @@ traitlets = "*" [[package]] name = "mdit-py-plugins" -version = "0.4.2" +version = "0.6.1" description = "Collection of plugins for markdown-it-py" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev", "docs"] files = [ - {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, - {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, + {file = "mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d"}, + {file = "mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0"}, ] [package.dependencies] -markdown-it-py = ">=1.0.0,<4.0.0" +markdown-it-py = ">=2.0.0,<5.0.0" [package.extras] code-style = ["pre-commit"] rtd = ["myst-parser", "sphinx-book-theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "pytest-timeout"] [[package]] name = "mdurl" @@ -2375,19 +2332,16 @@ files = [ [[package]] name = "mistune" -version = "3.2.1" +version = "3.3.3" description = "A sane and fast Markdown parser with useful plugins and renderers" optional = false python-versions = ">=3.8" groups = ["dev", "docs"] files = [ - {file = "mistune-3.2.1-py3-none-any.whl", hash = "sha256:78cdb0ba5e938053ccf63651b352508d2efa9411dc8810bfb05f2dc5140c0048"}, - {file = "mistune-3.2.1.tar.gz", hash = "sha256:7c8e5501d38bac1582e067e46c8343f17d57ea1aaa735823f3aba1fd59c88a28"}, + {file = "mistune-3.3.3-py3-none-any.whl", hash = "sha256:99de1585e42dcbd826faa9e11a202727a5e202e4e4722a4c69ac1ff615793dd7"}, + {file = "mistune-3.3.3.tar.gz", hash = "sha256:c4c6c0c840b8637a2e9b8b6d607eb7c8f00888bf14c754409bcd339e848c2477"}, ] -[package.dependencies] -typing-extensions = {version = "*", markers = "python_version < \"3.11\""} - [[package]] name = "more-itertools" version = "10.2.0" @@ -2431,30 +2385,30 @@ testing = ["beautifulsoup4", "coverage (>=6.4)", "ipykernel (>=5.5)", "ipython ( [[package]] name = "myst-parser" -version = "4.0.0" +version = "5.1.0" description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," optional = false -python-versions = ">=3.10" +python-versions = ">=3.11" groups = ["docs"] files = [ - {file = "myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d"}, - {file = "myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531"}, + {file = "myst_parser-5.1.0-py3-none-any.whl", hash = "sha256:9c91c52b3cdb4d94a6506e4fab4e2f296c7623a0da0dcbe6de1565c3dad67a8a"}, + {file = "myst_parser-5.1.0.tar.gz", hash = "sha256:ab69322dc6719dcc7f296479dbb70181b66df6ed315064f92dbc85c0e1bf2f02"}, ] [package.dependencies] -docutils = ">=0.19,<0.22" +docutils = ">=0.20,<0.23" jinja2 = "*" -markdown-it-py = ">=3.0,<4.0" -mdit-py-plugins = ">=0.4.1,<1.0" +markdown-it-py = ">=4.2,<5.0" +mdit-py-plugins = ">=0.6.1,<1.0" pyyaml = "*" -sphinx = ">=7,<9" +sphinx = ">=8,<10" [package.extras] -code-style = ["pre-commit (>=3.0,<4.0)"] +code-style = ["pre-commit (>=4.0,<5.0)"] linkify = ["linkify-it-py (>=2.0,<3.0)"] -rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] -testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] -testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] +rtd = ["ipython", "sphinx (>=8)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.13.0,<0.14.0)", "sphinxext-rediraffe (>=0.3.0,<0.4.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pygments (<2.21)", "pytest (>=9,<10)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest (>=0.3.0,<0.4.0)"] +testing-docutils = ["pygments", "pytest (>=9,<10)", "pytest-param-files (>=0.6.0,<0.7.0)"] [[package]] name = "nbclient" @@ -2583,19 +2537,20 @@ files = [ [[package]] name = "notebook" -version = "7.5.6" +version = "7.6.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "notebook-7.5.6-py3-none-any.whl", hash = "sha256:4dde3f8fb55fa8fb7946d58c6e869ce9baf46d00fc070664f62604569d0faca0"}, - {file = "notebook-7.5.6.tar.gz", hash = "sha256:621174aade80108f0020b0f00738000b215f75fa3cd90771ad7aa0f24536a4e1"}, + {file = "notebook-7.6.1-py3-none-any.whl", hash = "sha256:6ea1e4c926f0dc490444ddcc335797a3dacda470a805d01031872fb119988ba2"}, + {file = "notebook-7.6.1.tar.gz", hash = "sha256:0b45fd1010668dd4808c40914d957706dbf044a677d28764dc881b74dfcede82"}, ] [package.dependencies] -jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.5.7,<4.6" +jupyter-builder = ">=1.0.2,<2" +jupyter-server = ">=2.19.0,<3" +jupyterlab = ">=4.6.2,<4.7" jupyterlab-server = ">=2.28.0,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" @@ -2603,7 +2558,7 @@ tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0) ; python_version < \"3.10\"", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.28.0,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["ipykernel", "jupyter-server[test] (>=2.19.0,<3)", "jupyterlab-server[test] (>=2.28.0,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -2623,72 +2578,6 @@ jupyter-server = ">=1.8,<3" [package.extras] test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"] -[[package]] -name = "numpy" -version = "2.2.6" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.10" -groups = ["main", "dev", "docs"] -markers = "python_version < \"3.13\"" -files = [ - {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"}, - {file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"}, - {file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"}, - {file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"}, - {file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"}, - {file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"}, - {file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"}, - {file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"}, - {file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"}, - {file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"}, - {file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"}, - {file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"}, -] - [[package]] name = "numpy" version = "2.3.5" @@ -2696,7 +2585,6 @@ description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.11" groups = ["main", "dev", "docs"] -markers = "python_version >= \"3.13\"" files = [ {file = "numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10"}, {file = "numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218"}, @@ -2788,31 +2676,17 @@ files = [ [package.dependencies] sphinx = ">=6" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "overrides" -version = "7.7.0" -description = "A decorator to automatically detect mismatch when overriding a method." -optional = false -python-versions = ">=3.6" -groups = ["dev"] -markers = "python_version < \"3.12\"" -files = [ - {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, - {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, -] [[package]] name = "packaging" -version = "25.0" +version = "26.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev", "docs", "test"] files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, ] [[package]] @@ -2873,103 +2747,99 @@ ptyprocess = ">=0.5" [[package]] name = "pillow" -version = "12.2.0" +version = "12.3.0" description = "Python Imaging Library (fork)" optional = false python-versions = ">=3.10" groups = ["main", "dev", "docs"] files = [ - {file = "pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f"}, - {file = "pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97"}, - {file = "pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff"}, - {file = "pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec"}, - {file = "pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136"}, - {file = "pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c"}, - {file = "pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3"}, - {file = "pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa"}, - {file = "pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032"}, - {file = "pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5"}, - {file = "pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024"}, - {file = "pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab"}, - {file = "pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65"}, - {file = "pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7"}, - {file = "pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e"}, - {file = "pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705"}, - {file = "pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176"}, - {file = "pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b"}, - {file = "pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909"}, - {file = "pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808"}, - {file = "pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60"}, - {file = "pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe"}, - {file = "pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5"}, - {file = "pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421"}, - {file = "pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987"}, - {file = "pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76"}, - {file = "pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005"}, - {file = "pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780"}, - {file = "pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5"}, - {file = "pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5"}, - {file = "pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940"}, - {file = "pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5"}, - {file = "pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414"}, - {file = "pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c"}, - {file = "pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2"}, - {file = "pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c"}, - {file = "pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795"}, - {file = "pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f"}, - {file = "pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed"}, - {file = "pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9"}, - {file = "pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed"}, - {file = "pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3"}, - {file = "pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9"}, - {file = "pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795"}, - {file = "pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e"}, - {file = "pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b"}, - {file = "pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06"}, - {file = "pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b"}, - {file = "pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f"}, - {file = "pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612"}, - {file = "pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c"}, - {file = "pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea"}, - {file = "pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4"}, - {file = "pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4"}, - {file = "pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea"}, - {file = "pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24"}, - {file = "pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98"}, - {file = "pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453"}, - {file = "pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8"}, - {file = "pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b"}, - {file = "pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295"}, - {file = "pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed"}, - {file = "pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae"}, - {file = "pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601"}, - {file = "pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be"}, - {file = "pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f"}, - {file = "pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286"}, - {file = "pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50"}, - {file = "pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104"}, - {file = "pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7"}, - {file = "pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150"}, - {file = "pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1"}, - {file = "pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463"}, - {file = "pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3"}, - {file = "pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166"}, - {file = "pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe"}, - {file = "pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd"}, - {file = "pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e"}, - {file = "pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06"}, - {file = "pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43"}, - {file = "pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354"}, - {file = "pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1"}, - {file = "pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e"}, - {file = "pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5"}, + {file = "pillow-12.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a"}, + {file = "pillow-12.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7"}, + {file = "pillow-12.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f"}, + {file = "pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec"}, + {file = "pillow-12.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468"}, + {file = "pillow-12.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed"}, + {file = "pillow-12.3.0-cp310-cp310-win32.whl", hash = "sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1"}, + {file = "pillow-12.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb"}, + {file = "pillow-12.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f"}, + {file = "pillow-12.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756"}, + {file = "pillow-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6"}, + {file = "pillow-12.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd"}, + {file = "pillow-12.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd"}, + {file = "pillow-12.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c"}, + {file = "pillow-12.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5"}, + {file = "pillow-12.3.0-cp311-cp311-win32.whl", hash = "sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b"}, + {file = "pillow-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a"}, + {file = "pillow-12.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26"}, + {file = "pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965"}, + {file = "pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7"}, + {file = "pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9"}, + {file = "pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91"}, + {file = "pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c"}, + {file = "pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df"}, + {file = "pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f"}, + {file = "pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09"}, + {file = "pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510"}, + {file = "pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89"}, + {file = "pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace"}, + {file = "pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec"}, + {file = "pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66"}, + {file = "pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35"}, + {file = "pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65"}, + {file = "pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3"}, + {file = "pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a"}, + {file = "pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e"}, + {file = "pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f"}, + {file = "pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8"}, + {file = "pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b"}, + {file = "pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330"}, + {file = "pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217"}, + {file = "pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930"}, + {file = "pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8"}, + {file = "pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0"}, + {file = "pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321"}, + {file = "pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b"}, + {file = "pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198"}, + {file = "pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130"}, + {file = "pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a"}, + {file = "pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d"}, + {file = "pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838"}, + {file = "pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e"}, + {file = "pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17"}, + {file = "pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385"}, + {file = "pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c"}, + {file = "pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d"}, + {file = "pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931"}, + {file = "pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7"}, + {file = "pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c"}, + {file = "pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45"}, + {file = "pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139"}, + {file = "pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402"}, + {file = "pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c"}, + {file = "pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f"}, + {file = "pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701"}, + {file = "pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace"}, + {file = "pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4"}, + {file = "pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39"}, + {file = "pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71"}, + {file = "pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827"}, + {file = "pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5"}, + {file = "pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658"}, + {file = "pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf"}, + {file = "pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64"}, + {file = "pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e"}, + {file = "pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777"}, + {file = "pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1"}, + {file = "pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9"}, + {file = "pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8"}, + {file = "pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418"}, + {file = "pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a"}, + {file = "pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce"}, ] [package.extras] @@ -2977,7 +2847,7 @@ docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybut fpx = ["olefile"] mic = ["olefile"] test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"] -tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] +tests = ["coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "setuptools", "trove-classifiers (>=2024.10.12)"] xmp = ["defusedxml"] [[package]] @@ -3105,7 +2975,7 @@ files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] -markers = {dev = "os_name != \"nt\" or sys_platform != \"win32\"", docs = "sys_platform != \"win32\"", test = "sys_platform != \"win32\""} +markers = {dev = "sys_platform != \"win32\" or os_name != \"nt\"", docs = "sys_platform != \"win32\"", test = "sys_platform != \"win32\""} [[package]] name = "pure-eval" @@ -3137,14 +3007,14 @@ markers = {dev = "implementation_name != \"PyPy\"", docs = "implementation_name [[package]] name = "pydata-sphinx-theme" -version = "0.15.4" +version = "0.20.0" description = "Bootstrap-based Sphinx theme from the PyData community" optional = false -python-versions = ">=3.9" +python-versions = ">=3.11" groups = ["docs"] files = [ - {file = "pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6"}, - {file = "pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d"}, + {file = "pydata_sphinx_theme-0.20.0-py3-none-any.whl", hash = "sha256:56744483c9d72c783e075de716ab95d486108b69605df7528078090b73f11f69"}, + {file = "pydata_sphinx_theme-0.20.0.tar.gz", hash = "sha256:0da172d41e19a66de875f4002f7054b385372ec65763852193791e658d50bb4a"}, ] [package.dependencies] @@ -3152,17 +3022,17 @@ accessible-pygments = "*" Babel = "*" beautifulsoup4 = "*" docutils = "!=0.17.0" -packaging = "*" +jinja2 = "*" pygments = ">=2.7" -sphinx = ">=5" -typing-extensions = "*" +requests = "*" +sphinx = ">=8.2,<10" [package.extras] -a11y = ["pytest-playwright"] +a11y = ["pydata-sphinx-theme[test]"] dev = ["pandoc", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml", "sphinx-theme-builder[cli]", "tox"] -doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (>=1.4.1)", "sphinxext-rediraffe", "xarray"] +doc = ["ablog (>=0.11.8)", "astroid (>=3,!=4.0.3)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (==3.6.1)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap (<2.7.0)", "sphinx-togglebutton", "sphinxcontrib-mermaid", "sphinxcontrib-youtube (>=1.4.1)", "sphinxext-rediraffe", "xarray"] i18n = ["Babel", "jinja2"] -test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] +test = ["pytest", "pytest-cov", "pytest-playwright", "pytest-regressions", "sphinx[test]", "typing-extensions"] [[package]] name = "pyerfa" @@ -3224,24 +3094,22 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "9.0.3" +version = "9.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.10" groups = ["test"] files = [ - {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, - {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, ] [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} iniconfig = ">=1.0.1" packaging = ">=22" pluggy = ">=1.5,<2" pygments = ">=2.7.2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] @@ -3300,7 +3168,7 @@ description = "Python for Window Extensions" optional = false python-versions = "*" groups = ["dev", "docs", "test"] -markers = "platform_python_implementation != \"PyPy\" and sys_platform == \"win32\"" +markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\"" files = [ {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, {file = "pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b"}, @@ -3596,6 +3464,18 @@ files = [ {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"}, ] +[[package]] +name = "roman-numerals" +version = "4.1.0" +description = "Manipulate well-formed Roman numerals" +optional = false +python-versions = ">=3.10" +groups = ["docs"] +files = [ + {file = "roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7"}, + {file = "roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2"}, +] + [[package]] name = "rpds-py" version = "0.27.1" @@ -3761,71 +3641,6 @@ files = [ {file = "rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8"}, ] -[[package]] -name = "scipy" -version = "1.15.3" -description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.10\"" -files = [ - {file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"}, - {file = "scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253"}, - {file = "scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f"}, - {file = "scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92"}, - {file = "scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82"}, - {file = "scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40"}, - {file = "scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e"}, - {file = "scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c"}, - {file = "scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13"}, - {file = "scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b"}, - {file = "scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba"}, - {file = "scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65"}, - {file = "scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1"}, - {file = "scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889"}, - {file = "scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982"}, - {file = "scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9"}, - {file = "scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594"}, - {file = "scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb"}, - {file = "scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019"}, - {file = "scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6"}, - {file = "scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477"}, - {file = "scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c"}, - {file = "scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45"}, - {file = "scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49"}, - {file = "scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e"}, - {file = "scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539"}, - {file = "scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed"}, - {file = "scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759"}, - {file = "scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62"}, - {file = "scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb"}, - {file = "scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730"}, - {file = "scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825"}, - {file = "scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7"}, - {file = "scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11"}, - {file = "scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126"}, - {file = "scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163"}, - {file = "scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8"}, - {file = "scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5"}, - {file = "scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e"}, - {file = "scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb"}, - {file = "scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723"}, - {file = "scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb"}, - {file = "scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4"}, - {file = "scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5"}, - {file = "scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca"}, - {file = "scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf"}, -] - -[package.dependencies] -numpy = ">=1.23.5,<2.5" - -[package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] - [[package]] name = "scipy" version = "1.17.1" @@ -3833,7 +3648,6 @@ description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.11" groups = ["main"] -markers = "python_version >= \"3.11\"" files = [ {file = "scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec"}, {file = "scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696"}, @@ -3923,27 +3737,6 @@ nativelib = ["pyobjc-framework-Cocoa ; sys_platform == \"darwin\"", "pywin32 ; s objc = ["pyobjc-framework-Cocoa ; sys_platform == \"darwin\""] win32 = ["pywin32 ; sys_platform == \"win32\""] -[[package]] -name = "setuptools" -version = "78.1.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "setuptools-78.1.1-py3-none-any.whl", hash = "sha256:c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561"}, - {file = "setuptools-78.1.1.tar.gz", hash = "sha256:fcc17fd9cd898242f6b4adfaca46137a9edef687f43e6f78469692a5e70d851d"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] -core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] - [[package]] name = "six" version = "1.17.0" @@ -3958,25 +3751,25 @@ files = [ [[package]] name = "skycalc-ipy" -version = "0.7.0" +version = "0.8.0" description = "Get atmospheric spectral information from the ESO skycalc server." optional = false -python-versions = "<3.15,>=3.10" +python-versions = "<3.15,>=3.12" groups = ["main"] files = [ - {file = "skycalc_ipy-0.7.0-py3-none-any.whl", hash = "sha256:1c51688f89ce360c3248a35c43d672594d3063e4faaa9426e7bf881c8f50846d"}, - {file = "skycalc_ipy-0.7.0.tar.gz", hash = "sha256:96580d64e0dfd332fb5093ea6d1d44a7b7186ef50e0b290e94473fd3a86f85de"}, + {file = "skycalc_ipy-0.8.0-py3-none-any.whl", hash = "sha256:f10af694457a806f44160bf7b3ecad485f7ae892ab08c7f52b6b44d3f28be217"}, + {file = "skycalc_ipy-0.8.0.tar.gz", hash = "sha256:fb8bb7bb8dfdf1ef0ec2a198ae23c065a45468579a0f876ebc688eeeaab37d8f"}, ] [package.dependencies] -astar-utils = ">=0.5.0,<1.0" -astropy = ">=6.1.7,<8.0.0" +astar-utils = ">=0.7.0,<1.0" +astropy = ">=7.0.2,<9.0.0" httpx = ">=0.28.1,<1.0" -numpy = ">=1.26.4,<2.4.0" +numpy = ">=2.2.6,<3.0.0" pyyaml = ">=6.0.3,<7.0.0" [package.extras] -syn = ["scipy (>=1.15.3,<1.16.0) ; python_version == \"3.10\"", "scipy (>=1.17.0,<1.18.0) ; python_version >= \"3.11\"", "synphot (>=1.6.1,<2.0.0)"] +syn = ["synphot (>=1.7.0,<2.0.0)"] [[package]] name = "sniffio" @@ -4004,38 +3797,39 @@ files = [ [[package]] name = "soupsieve" -version = "2.7" +version = "2.8.4" description = "A modern CSS selector implementation for Beautiful Soup." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main", "dev", "docs"] files = [ - {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, - {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, + {file = "soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65"}, + {file = "soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e"}, ] [[package]] name = "sphinx" -version = "8.1.3" +version = "9.1.0" description = "Python documentation generator" optional = false -python-versions = ">=3.10" +python-versions = ">=3.12" groups = ["docs"] files = [ - {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, - {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, + {file = "sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978"}, + {file = "sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb"}, ] [package.dependencies] alabaster = ">=0.7.14" babel = ">=2.13" colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} -docutils = ">=0.20,<0.22" +docutils = ">=0.21,<0.23" imagesize = ">=1.3" Jinja2 = ">=3.1" packaging = ">=23.0" Pygments = ">=2.17" requests = ">=2.30.0" +roman-numerals = ">=1.0.0" snowballstemmer = ">=2.2" sphinxcontrib-applehelp = ">=1.0.7" sphinxcontrib-devhelp = ">=1.0.6" @@ -4043,32 +3837,26 @@ sphinxcontrib-htmlhelp = ">=2.0.6" sphinxcontrib-jsmath = ">=1.0.1" sphinxcontrib-qthelp = ">=1.0.6" sphinxcontrib-serializinghtml = ">=1.1.9" -tomli = {version = ">=2", markers = "python_version < \"3.11\""} - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] -test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] [[package]] name = "sphinx-book-theme" -version = "1.1.4" +version = "1.4.0" description = "A clean book theme for scientific explanations and documentation with Sphinx" optional = false -python-versions = ">=3.9" +python-versions = ">=3.11" groups = ["docs"] files = [ - {file = "sphinx_book_theme-1.1.4-py3-none-any.whl", hash = "sha256:843b3f5c8684640f4a2d01abd298beb66452d1b2394cd9ef5be5ebd5640ea0e1"}, - {file = "sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed"}, + {file = "sphinx_book_theme-1.4.0-py3-none-any.whl", hash = "sha256:10fd57d667ee0b565928c3ce95562e281c5d1b94bd5a2aafc5cd41a5d498dcd0"}, + {file = "sphinx_book_theme-1.4.0.tar.gz", hash = "sha256:fee3cd70573d9a8cd4ab380a3cb1f11139c24dcaa2cf47d06d7f924f1b44eaff"}, ] [package.dependencies] -pydata-sphinx-theme = "0.15.4" -sphinx = ">=6.1" +pydata-sphinx-theme = "0.20.0" +sphinx = ">=8.2" [package.extras] code-style = ["pre-commit"] -doc = ["ablog", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"] +doc = ["ablog (>=0.11.13)", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"] test = ["beautifulsoup4", "coverage", "defusedxml", "myst-nb", "pytest", "pytest-cov", "pytest-regressions", "sphinx_thebe"] [[package]] @@ -4174,14 +3962,14 @@ test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-mermaid" -version = "2.0.2" +version = "2.1.0" description = "Mermaid diagrams in your Sphinx-powered docs" optional = false python-versions = ">=3.10" groups = ["docs"] files = [ - {file = "sphinxcontrib_mermaid-2.0.2-py3-none-any.whl", hash = "sha256:d862e514991279fb4816302c5cfe167d2557bf3ce7125ae0cb47dac80a0f46ce"}, - {file = "sphinxcontrib_mermaid-2.0.2.tar.gz", hash = "sha256:f09576c78ca93fa0e3034fd9c45aaffa7c44ab449de9c43b8b8d262afe52bc66"}, + {file = "sphinxcontrib_mermaid-2.1.0-py3-none-any.whl", hash = "sha256:417cd144ec4b28852f46ba653f02ce8e538881c812111671a4c30344e87f2112"}, + {file = "sphinxcontrib_mermaid-2.1.0.tar.gz", hash = "sha256:13c5f9ac395cb6abf403eca34e228dc9fb3a30c9d960dbf3e40e9a8cef969549"}, ] [package.dependencies] @@ -4336,38 +4124,38 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "synphot" -version = "1.6.1" +version = "1.7.0" description = "Synthetic photometry" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "synphot-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0ac5d550f8e906685d6ae842a84376e315c4689c1b97780551256d84246c1a72"}, - {file = "synphot-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d1e46fe6da16c1c4bbd7aeecb1995ddb56f67f8f255af69f9e840595acfeaf58"}, - {file = "synphot-1.6.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b458023f80c5e703bee8098ff5a408ef78b55e35dc786d05bfd3c44792a6e6b7"}, - {file = "synphot-1.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58fe1cd739ce365fdb13892b057eb8eab89f955d21d8e0444676df15cd32bb5a"}, - {file = "synphot-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:66818542490b8b7c6edf1c7650da80d2ad754a91f2392cfc2124ba8c08d043f2"}, - {file = "synphot-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ca893e5d694ac7bef358a30c5933fdec3a06a006e60567acc6858827778ff01b"}, - {file = "synphot-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad0ea8ae1ed245d42d265931f30d7f6c6ed62ab6639c29e3d55b6d03103e61d2"}, - {file = "synphot-1.6.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:26fd407171f4fb01a10ddb8fe7215d92e6c9d4bcef2c4a38cbc8a848e30a6722"}, - {file = "synphot-1.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:434fd8969c389c9cdc6dfe49f915ba2144218e7b8e3149125c6ecf42ccd279d2"}, - {file = "synphot-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a9f751fc55c5ff137d3010f3a4cb5436b4d07ff5aac9562cc342bdd53db0dbf6"}, - {file = "synphot-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:38de46c7eaa98bb8173aa5c8e58bdc5ef68b8da2c939b49d4f847761178b9613"}, - {file = "synphot-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b706cb326fbe95cfbf43345c6a28244c026204f9fcaf532d6268b35ec53d50e1"}, - {file = "synphot-1.6.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f956033a3218c4cda459878dc22b9ec7e240b09efd5bef0881b0addb59d51faf"}, - {file = "synphot-1.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e09b21fed5fe83072fb7fa4f03e83f862224e4e12224df8b909a800563255201"}, - {file = "synphot-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e0948891ef30264a6bbe750bfdc582cb6e6de2f1b8ce9740348cc2815436c941"}, - {file = "synphot-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b551a8194cbca165d252b8b232364a5aff0b1e2751313b294c76bcb7fa213b8"}, - {file = "synphot-1.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f1681f9fe06aeaaa0044fa98801bf0fffb521b823c9b46895b7155b4da74f075"}, - {file = "synphot-1.6.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c120eb4d196819592b900da44f6f67bce66de248d30ee6376b2df21d98656bff"}, - {file = "synphot-1.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fdf41e8e4c2f443f73555a2e728436c987b1d0a23baeaaa477c40e4364e4af61"}, - {file = "synphot-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:212ad95cb1f664d593ccf6a91573e8dca46fe85b260da3c31883df346c1f586f"}, - {file = "synphot-1.6.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d61432c06fb26c6bd2a294edc4fee09e992d8e2b4f847140a5c97e8fa4d67f5"}, - {file = "synphot-1.6.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:acb6691b6ab0e5b7fa23e1a98e4bf778a8ab18c19f73f5e02d4311537a01eaa9"}, - {file = "synphot-1.6.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ce8898237eb264453793ecd95b6e44f8f7d0516e753b029eee245e642de640c"}, - {file = "synphot-1.6.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e7f395226e1103c3469a5747fdaf5e13036e6228467a9c39b895acd811ab163"}, - {file = "synphot-1.6.1-cp314-cp314-win_amd64.whl", hash = "sha256:bbcb7f68384b6a6c1981109df9720406adfdd15b455b12a3bd88d082a9832bbd"}, - {file = "synphot-1.6.1.tar.gz", hash = "sha256:eb7c63c450c6cc5ef8db0c9dd41cd95a4647c95f4769acf1d30bb89061a8358b"}, + {file = "synphot-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8cf368c83064f9131a31c19ebc2cd4843888d8447d99fc407625e186009235fa"}, + {file = "synphot-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b2a703ddfd9f4136c9c97d4c96631a05caefbf08015166b2a3abcdfa8648db0"}, + {file = "synphot-1.7.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7560121f6207cba25cbce4573f23cb0163594a72e893edcf82aaa121dee468bc"}, + {file = "synphot-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5bbe1793a56fd6a7dfabb1b5bb81d3c767fec8034909a3ec7b634901f6ddc2f4"}, + {file = "synphot-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:e0d7798cb23442d1f6d118a9744ac509bc6b77b531ad6792066cbdc18510ed09"}, + {file = "synphot-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f4d8fa3681ee71e7e5b2f63d92849bed7836e099b75ac0b6489eb98b25020dde"}, + {file = "synphot-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:39fa4ba3783ea9b04595e4692ca67e7a083cdc4da8f56e0f37c8e7401a2ce2b5"}, + {file = "synphot-1.7.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70c29b04495f25f8efdbb97edbd8fea716d2bafd50bc6e14cb30b95faafd0ca7"}, + {file = "synphot-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0317763976e264bcff535dc29f93b5a29300eb757629d31f04edcd0a98635901"}, + {file = "synphot-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:60486a6d5887e0f296c16415a73bc231d0fc0211a0543859125a94301e0e59b0"}, + {file = "synphot-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cefe738d1e807a7b79d4bee62df02411dbe25f7f4eeccd91d7b80dc51bcad7c6"}, + {file = "synphot-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c77ffd51ffed10dbad202c64673596d4d4f167534e1e51ad1a4b484297e1736a"}, + {file = "synphot-1.7.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:79e959f44944200725db60925dd89bac3ef57c9ccd57ef74cd63333be215b108"}, + {file = "synphot-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:79139f14a90c6bf6bf1a639f4b2b41f8015a9915388f26b3dac9e1e0b179df14"}, + {file = "synphot-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:a5dd6b012bca6244ee64e4793ffde8024999633601b94f50b077098264771f9e"}, + {file = "synphot-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4420f88b85276e2fe7f7a5c9e5d18bc66a49c36a55e360399e4fda75f2af4e51"}, + {file = "synphot-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90790ddede7354b2964f3015ae8e57344ceab2a3e1a50d76365123880f6c941e"}, + {file = "synphot-1.7.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6bf64e62a6a34bc4072a6f11ab9671283fa3211ea8b71a9345eb006dabb70a4d"}, + {file = "synphot-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e81280915f944213ec2d5f56c6d4dcaf3f59901c9aa8dac86bba1e088abd0e63"}, + {file = "synphot-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:5afb08da9a634af85dbbb753ba70ac468baeeff2bd02d7cb4fd08a7df4dbe701"}, + {file = "synphot-1.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7d57b670cd1e1d1a820ca32b478b62f3e2550bb1b119d3ad6c3fa894ee8e0e3a"}, + {file = "synphot-1.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9062121c87f4287cba8dcc29bab05bc1a1f471219760b90741f8732922baf4e7"}, + {file = "synphot-1.7.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:43e18635ab59d0334f0f2f32cbc828a79851e36475a974de97fcbd332f227048"}, + {file = "synphot-1.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:452167a1bb67354b92b28dfebd539ffa55267725dd2f0e254a18ccf55645482b"}, + {file = "synphot-1.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:1cc51eec85bcc6869a0c779200f449a2769f6ded48a76c6f24cad8498decf81b"}, + {file = "synphot-1.7.0.tar.gz", hash = "sha256:acd0fff408008a93c43bfbaf27e7c8ccee271d644d62152e4ad2b7462619f9dc"}, ] [package.dependencies] @@ -4436,60 +4224,46 @@ webencodings = ">=0.4" doc = ["sphinx", "sphinx_rtd_theme"] test = ["flake8", "isort", "pytest"] -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -groups = ["dev", "docs", "test"] -markers = "python_version == \"3.10\"" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "tornado" -version = "6.5.7" +version = "6.5.8" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.9" groups = ["dev", "docs", "test"] files = [ - {file = "tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163"}, - {file = "tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100"}, - {file = "tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972"}, - {file = "tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b"}, - {file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92"}, - {file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5"}, - {file = "tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4"}, - {file = "tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4"}, - {file = "tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796"}, - {file = "tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2"}, + {file = "tornado-6.5.8-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:cc6aa787d7cfab7c3d35189dc7a56fbd2399a569624c730c6b55b3d6531d0403"}, + {file = "tornado-6.5.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9715b5eb79735b2bcd454ce216a9275b7c0470e64ea1bf5742f78b2f72b26eeb"}, + {file = "tornado-6.5.8-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:547d63f450d570c14fe0e8db2cfb14c9bbd1c2503b4a6612586267955aa47b58"}, + {file = "tornado-6.5.8-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e2360a0ffbe145eca8af0b19cb7203d79b1a98dd4cccdd6b368f6f49c2e3808"}, + {file = "tornado-6.5.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5d242290bdf7ab3151bc1065fdd75c0dcc21cbc7b49f22a4c56329c2d6566d22"}, + {file = "tornado-6.5.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7b94ff0e128fe0542f3bd331fb44d06260fc4ac16881545159f34ef08aad4195"}, + {file = "tornado-6.5.8-cp39-abi3-win32.whl", hash = "sha256:67832909c4779c64942380cb5f044a5c6163d00831472d80e25e115de9917836"}, + {file = "tornado-6.5.8-cp39-abi3-win_amd64.whl", hash = "sha256:11881db6b7c168494be2c2d12e65931451bdf7ee718535418ae1d8855dd5a0ee"}, + {file = "tornado-6.5.8-cp39-abi3-win_arm64.whl", hash = "sha256:68a7468c7e289f8514d7d664101753903217eff1bb6822c6b5994a0b5f5bcb26"}, + {file = "tornado-6.5.8.tar.gz", hash = "sha256:9452e1b208a8bd771e2cb1f2ff564985b9b214bdebbe622793e1799e0a6bd23f"}, ] [[package]] name = "tqdm" -version = "4.67.3" +version = "4.70.0" description = "Fast, Extensible Progress Meter" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf"}, - {file = "tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb"}, + {file = "tqdm-4.70.0-py3-none-any.whl", hash = "sha256:7f585706bfddbdebf89daac705b2dfcc16890130727d3197ca62c732b4310953"}, + {file = "tqdm-4.70.0.tar.gz", hash = "sha256:55b0b0dbd97462d06ebee91e4dac24ed4d4702be82b24f07e6c1d27e08cea220"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] -discord = ["requests"] +discord = ["envwrap", "requests"] notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] +slack = ["envwrap", "slack-sdk"] +telegram = ["envwrap", "requests"] [[package]] name = "traitlets" @@ -4521,14 +4295,14 @@ files = [ [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.16.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["main", "dev", "docs", "test"] files = [ - {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, - {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, + {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"}, + {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"}, ] [[package]] @@ -4651,5 +4425,5 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" -python-versions = ">=3.10,<3.15" -content-hash = "9d466fdf8137d22f15be3a77237a6a8bd2d3f344d34a7c95f18d02f3d56f0192" +python-versions = ">=3.12,<3.15" +content-hash = "ed6ef2a6cb8814ee6ab96c983711908f3102cbfdeb28136410bf675ad2da745c" diff --git a/pyproject.toml b/pyproject.toml index cb4ecb884..da766c6da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.11.5a3" +version = "0.12.0b2.dev5" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ @@ -14,30 +14,28 @@ maintainers = [ readme = "README.md" keywords = [ "astronomy" ] dynamic = [ "classifiers" ] -requires-python = ">=3.10,<3.15" +requires-python = ">=3.12,<3.15" dependencies = [ - "numpy (>=1.26.4,<2.3.0) ; python_version >= '3.10' and python_version < '3.13'", - "numpy (>=2.3.5,<3.0.0) ; python_version >= '3.13'", - "scipy (>=1.15.3,<1.16.0) ; python_version >= '3.10' and python_version < '3.11'", - "scipy (>=1.17.0,<1.18.0) ; python_version >= '3.11'", + "numpy (>=2.2.6,<3.0.0)", + "scipy (>=1.17.0,<1.18.0)", - "astropy (>=6.1.7,<8.0.0)", - "matplotlib (>=3.10.5,<4.0.0)", - "pooch (>=1.8.2,<2.0.0)", + "astropy (>=7.2.2,<9.0.0)", + "matplotlib (>=3.11.1,<4.0.0)", + "pooch (>=1.9.0,<2.0.0)", - "docutils (>=0.19,<1.0)", + "docutils (>=0.22,<1.0)", "httpxyz (>=0.31.2,<1.0)", - "beautifulsoup4 (>=4.13.3,<5.0.0)", - "lxml (>=6.0.2,<7.0.0)", + "beautifulsoup4 (>=4.15.0,<5.0.0)", + "lxml (>=6.1.1,<7.0.0)", "pyyaml (>=6.0.3,<7.0.0)", "more-itertools (>=10.2.0,<11.0.0)", - "tqdm (>=4.66.6,<5.0.0)", - "packaging (>=25.0,<27.0)", + "tqdm (>=4.70.0,<5.0.0)", + "packaging (>=26.0,<28.0)", - "synphot (>=1.6.1,<2.0.0)", - "skycalc-ipy (>=0.7.0,<1.0)", - "anisocado (>=0.5.0,<1.0)", - "astar-utils (>=0.5.3,<1.0)" + "synphot (>=1.7.0,<2.0.0)", + "skycalc-ipy (>=0.8.0,<1.0)", + "anisocado (>=0.6.0,<1.0)", + "astar-utils (>=0.7.0,<1.0)" ] [project.urls] @@ -59,12 +57,12 @@ optional = true [tool.poetry.group.dev.dependencies] jupyter = "^1.1.1" -jupytext = "^1.19.3" +jupytext = "^1.19.5" ipykernel = "^7.3.0" ipympl = "^0.10.0" [tool.poetry.group.test.dependencies] -pytest = "^9.0.3" +pytest = "^9.1.1" pytest-cov = "^7.1.0" ipykernel = "^7.3.0" @@ -72,13 +70,13 @@ ipykernel = "^7.3.0" optional = true [tool.poetry.group.docs.dependencies] -sphinx = "^8.1.3" -sphinx-book-theme = "^1.1.4" +sphinx = "^9.1.0" +sphinx-book-theme = "^1.4.0" jupyter-sphinx = ">=0.5.3" sphinx-copybutton = ">=0.5.2,<1.0" myst-nb = "^1.4.0" sphinxcontrib-apidoc = ">=0.6.0,<1.0" -sphinxcontrib-mermaid = "^2.0" +sphinxcontrib-mermaid = "^2.1" nbsphinx = "^0.9.8" numpydoc = "^1.10.0" ipykernel = "^7.3.0" diff --git a/scopesim/__init__.py b/scopesim/__init__.py index 17a330302..f081435df 100644 --- a/scopesim/__init__.py +++ b/scopesim/__init__.py @@ -83,6 +83,5 @@ list_example_data, download_example_data, ) -from .server.database import download_package # remove in v0.12 from .tests.mocks.load_basic_instrument import load_example_optical_train, example_simulation diff --git a/scopesim/defaults.yaml b/scopesim/defaults.yaml index 9c9cf8f75..b561bfa18 100644 --- a/scopesim/defaults.yaml +++ b/scopesim/defaults.yaml @@ -32,6 +32,10 @@ properties : preload_field_of_views : False nan_fill_value: 0. + psf: + inter_psf: True + interp_order: 1 + file : local_packages_path : "./inst_pkgs/" server_base_url : "https://scopesim.univie.ac.at/InstPkgSvr/" diff --git a/scopesim/effects/data_container.py b/scopesim/effects/data_container.py index f473886c9..217eeec07 100644 --- a/scopesim/effects/data_container.py +++ b/scopesim/effects/data_container.py @@ -64,6 +64,12 @@ class DataContainer: needed in order to save on memory usage. `._file` contains a pointer to the data open FITS file. + + .. versionchanged:: 0.12.0 + + Passing the old-style `file_name` argument now raises a ValueError. + Please use the new-style `filename` instead. + """ meta = None @@ -83,11 +89,9 @@ def __init__( # from scopesim import UserCommands # self.cmds = UserCommands() - if filename is None and "file_name" in kwargs: - warn("The 'file_name' kwarg is deprecated and will raise an error " - "from version 0.12 onwards, please use 'filename' instead!", - DeprecationWarning, stacklevel=2) - filename = kwargs["file_name"] + if "file_name" in kwargs: + raise ValueError("The 'file_name' kwarg is deprecated, please use " + "'filename' instead!") # A !-string filename is immediately resolved, but the file is not yet # searched for. This makes sense, as the filename should end up in the diff --git a/scopesim/effects/fits_headers.py b/scopesim/effects/fits_headers.py index 590a3b2ab..9110f3331 100644 --- a/scopesim/effects/fits_headers.py +++ b/scopesim/effects/fits_headers.py @@ -402,6 +402,10 @@ def flatten_dict( # Catch any value+comments lists/tuples match val: + # This became necessary in numpy 2.5, to avoid tripping on scalar quantities + case u.Quantity() as value: + # value get bound to variable anyway + comment = "" case [value, str(comment)]: pass # values get bound to variables anyway case value: diff --git a/scopesim/effects/illumination.py b/scopesim/effects/illumination.py index eaa340854..adf0b8851 100644 --- a/scopesim/effects/illumination.py +++ b/scopesim/effects/illumination.py @@ -5,6 +5,7 @@ from collections.abc import Callable, Mapping import numpy as np +from numpy.typing import NDArray from astropy import units as u from astropy.modeling.functional_models import Gaussian2D @@ -22,7 +23,7 @@ def gaussian2d( mu: tuple[float, float] = (0.0, 0.0), sigma: tuple[float, float] = (2000.0, 2000.0), theta: u.Quantity[u.deg] | float = 0.0 * u.deg, -) -> np.ndarray: +) -> NDArray: """ 2D elliptical Gaussian to be used for vignetting map. @@ -45,7 +46,7 @@ def gaussian2d( Returns ------- - np.ndarray + NDArray Vignetting map. """ @@ -71,7 +72,7 @@ def quadratic_vignetting( r_ref: float | None = None, mu: tuple[float, float] = (0.0, 0.0), stretch: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0), -) -> np.ndarray: +) -> NDArray: """ Quadratic vignetting pattern with independent stretch factors. @@ -97,7 +98,7 @@ def quadratic_vignetting( Returns ------- - np.ndarray + NDArray Vignetting map. """ diff --git a/scopesim/effects/metis_lms_trace_list.py b/scopesim/effects/metis_lms_trace_list.py index bf8c0d88f..877360da0 100644 --- a/scopesim/effects/metis_lms_trace_list.py +++ b/scopesim/effects/metis_lms_trace_list.py @@ -3,6 +3,7 @@ import copy import warnings +from functools import lru_cache from tqdm.auto import tqdm import numpy as np @@ -16,9 +17,11 @@ from ..utils import from_currsys, find_file, quantify, get_logger from .spectral_trace_list import SpectralTraceList -from .spectral_trace_list_utils import SpectralTrace -from .spectral_trace_list_utils import Transform2D -from .spectral_trace_list_utils import make_image_interpolations +from .spectral_trace_list_utils import ( + SpectralTrace, + Transform2D, + make_image_interpolations, +) from .apertures import ApertureMask from .ter_curves import TERCurve from ..optics.fov import FieldOfView, FieldOfView3D @@ -28,6 +31,17 @@ logger = get_logger(__name__) +@lru_cache(maxsize=8) +def _read_detector_layout(filename): + """Read (and cache) a detector layout file. + + The layout is required by ``MetisLMSSpectralTrace.fov_grid``, which is + called several times for each of the 28 slices; without the cache the + same file is read and parsed from disk on every call. + """ + return ioascii.read(find_file(filename)) + + class MetisLMSSpectralTraceList(SpectralTraceList): """SpectralTraceList for the METIS LM spectrograph.""" @@ -38,6 +52,7 @@ class MetisLMSSpectralTraceList(SpectralTraceList): "pixscale": 0.0082, # arcsec "grat_spacing": 18.2, "fp2_platescale": 0.303, + "fit_inverse": True, } def __init__(self, **kwargs): @@ -113,28 +128,44 @@ def apply_to(self, obj, **kwargs): slicecube = np.zeros((n_z, ny_slice, n_x)) for islice in range(n_z): - ifov = RectBivariateSpline(np.arange(n_y), - np.arange(n_x), - fovcube[islice], kx=1, ky=1) + ifov = RectBivariateSpline( + np.arange(n_y), + np.arange(n_x), + fovcube[islice], + kx=1, + ky=1, + ) slicecube[islice] = ifov(yfov, xfov, grid=False) - slicefov = FieldOfView3D(obj.header, - [obj.meta["wave_min"], - obj.meta["wave_max"]]) + slicefov = FieldOfView3D( + obj.header, + [obj.meta["wave_min"], obj.meta["wave_max"]], + ) slicefov.detector_header = obj.detector_header slicefov.meta["xi_min"] = obj.meta["xi_min"] slicefov.meta["xi_max"] = obj.meta["xi_max"] slicefov.meta["trace_id"] = sptid - slicefov.cube = fits.ImageHDU(header=slicewcs.to_header(), - data=slicecube) + slicefov.cube = fits.ImageHDU( + header=slicewcs.to_header(), + data=slicecube, + ) # slicefov.cube.writeto(f"slicefov_{sptid}.fits", overwrite=True) - slicefov.hdu = spt.map_spectra_to_focal_plane(slicefov) - if slicefov.hdu is not None: - sxmin = slicefov.hdu.header["XMIN"] - sxmax = slicefov.hdu.header["XMAX"] - symin = slicefov.hdu.header["YMIN"] - symax = slicefov.hdu.header["YMAX"] - fovimage[symin:symax, sxmin:sxmax] += slicefov.hdu.data + + try: + # If footprint is outside FOV, this will assign None to the hdu + slicefov.hdu = spt.map_spectra_to_focal_plane(slicefov) + except ValueError as err: # xlim_mm is None + logger.error(err) + continue + + if slicefov.hdu is None: # footprint outside FOV + continue + + sxmin = slicefov.hdu.header["XMIN"] + sxmax = slicefov.hdu.header["XMAX"] + symin = slicefov.hdu.header["YMIN"] + symax = slicefov.hdu.header["YMAX"] + fovimage[symin:symax, sxmin:sxmax] += slicefov.hdu.data obj.hdu = fits.ImageHDU(data=fovimage, header=obj.detector_header) @@ -150,11 +181,13 @@ def make_spectral_traces(self): self.meta["predisperser"] = tempres["Predisperser"] spec_traces = {} - for sli in np.arange(self.meta["nslice"]): - slicename = "Slice " + str(sli + 1) + for sli in range(self.meta["nslice"]): + slicename = f"Slice {sli + 1}" spec_traces[slicename] = MetisLMSSpectralTrace( self._file, - spslice=sli, params=self.meta) + spslice=sli, + params=self.meta, + ) self.spectral_traces = spec_traces @@ -177,6 +210,10 @@ def rectify_cube(self, hdulist, xi_min=None, xi_max=None, interps=None, If provided, there must be one for each image extension in `hdulist`. The functions go from pixels to the images and can be created with, e.g., RectBivariateSpline. + fit_inverse : bool [default True] + By default the method fits the inverse transform to be the optimal + inverse of the forward transform. Set `fit_inverse=False` to use the + predefined inverse matrix transform. """ try: inhdul = fits.open(hdulist) @@ -186,8 +223,12 @@ def rectify_cube(self, hdulist, xi_min=None, xi_max=None, interps=None, # Create interpolation functions if interps is None: logger.info("Computing interpolation functions") + # TODO: Allow cubic interpolation? interps = make_image_interpolations(inhdul, kx=1, ky=1) + # Use fitted inverse or the matrices? + fit_inverse = kwargs.get("fit_inverse", True) + # Create a common wcs for the rectification dwave = from_currsys("!SIM.spectral.spectral_bin_width", self.cmds) xi_min = np.min(self.slicelist["left"]) @@ -214,10 +255,25 @@ def rectify_cube(self, hdulist, xi_min=None, xi_max=None, interps=None, for i, spt in enumerate(self.spectral_traces.values()): spt.wave_min = wave_min spt.wave_max = wave_max - result = spt.rectify(hdulist, interps=interps, - wave_min=wave_min, wave_max=wave_max, - xi_min=xi_min, xi_max=xi_max, - bin_width=dwave) + + try: + result = spt.rectify( + hdulist, + interps=interps, + wave_min=wave_min, + wave_max=wave_max, + xi_min=xi_min, + xi_max=xi_max, + bin_width=dwave, + fit_inverse=fit_inverse, + ) + except (KeyError, ValueError) as err: + logger.error(err) + continue + + if result is None: # Outside filter range + continue + cube[:, i, :] = result.data.T # FIXME: use wcs object here @@ -285,7 +341,7 @@ def __init__(self, hdulist, spslice, params, **kwargs): super().__init__(polyhdu, **params) self._file = hdulist - self.meta["description"] = "Slice " + str(spslice + 1) + self.meta["description"] = f"Slice {spslice + 1}" self.meta["trace_id"] = f"Slice {spslice + 1}" self.meta.update(params) # Provisional: @@ -313,7 +369,7 @@ def fov_grid(self): y_max = aperture["top"] filename_det_layout = from_currsys("!DET.layout.file_name", cmds=self.cmds) - layout = ioascii.read(find_file(filename_det_layout)) + layout = _read_detector_layout(filename_det_layout) det_lims = {} xhw = layout["pixel_size"] * layout["x_size"] / 2 yhw = layout["pixel_size"] * layout["y_size"] / 2 diff --git a/scopesim/effects/metis_wcu/metis_wcu.py b/scopesim/effects/metis_wcu/metis_wcu.py index 02086fa57..725ed9230 100644 --- a/scopesim/effects/metis_wcu/metis_wcu.py +++ b/scopesim/effects/metis_wcu/metis_wcu.py @@ -6,6 +6,7 @@ """ from typing import ClassVar +from collections.abc import Iterable import numpy as np from astropy.table import Table @@ -200,6 +201,46 @@ def set_lamp(self, lamp="bb"): self.compute_lamp_emission() self.compute_fp_emission() + def tune_laser(self, wavelength, force=False): + """Set the wavelength(s) of the tunable laser + + Parameters + ========== + wavelength : float, list, array [um] + The wavelength(s) at which the laser emits + force: boolean [True] <- currently not used + If True bypass the validation for the lasers available + wavelength range. + """ + if not isinstance(wavelength, Iterable): + wavelength = [wavelength] + if not force: + wavelength = self._validate_tunable(wavelength) + + # if nothing's there use a dummy value outside any range + # to prevent division by zero + if len(wavelength) == 0: + wavelength = [0.001] + + self.meta["laser_t_wave"] = wavelength + self.compute_lamp_emission() + self.compute_fp_emission() + + def _validate_tunable(self, wave): + """Validate wave against a wavelength range + + This is currently not used as there is no reliable + information on the tuning range of the QCL. + """ + lam_min, lam_max = self.meta["laser_t_wave_limits"] + + accept = [w for w in wave if (w >= lam_min) & (w <= lam_max)] + n_rejected = len(wave) - len(accept) + if n_rejected > 0: + logger.warning("Removed %d wavelengths outside allowed range (%.2f, %.2f) um", + n_rejected, lam_min, lam_max) + return accept + def get_wavelength(self): """Try to set the appropriate wavelength vector for mode and filter.""" # Need to provide for wcu_lms_extended @@ -402,7 +443,7 @@ def compute_lamp_emission(self): # background emission from integrating sphere self.is_bg = BlackBody(self.is_temp, scale=self.bb_scale) - self.intens_bg = self.rho_is(lam) * self.is_bg(lam) + self.intens_bg = self.is_bg(lam) self.intensity = self.intens_lamp + self.intens_bg @@ -423,40 +464,55 @@ def _laser_intensity(self): The function computes all three lasers at once. This is possible because the lines are so far apart that there is no band that sees them both. """ - print("Computing laser intensity") + logger.info("Computing laser intensity") lam = self.wavelength dlam = lam[1] - lam[0] mult_is = self.is_multiplication(lam) # Laser 1 (L band) # TODO move to yaml - lamc_l = 3.39 * u.um - power_l = 5e-3 * u.W / (c.c * c.h / lamc_l) * u.ph + lamc_l = self.meta["laser_l_wave"] * u.um + power_l = self.meta["laser_l_power"] * u.W / (c.c * c.h / lamc_l) * u.ph # Laser 2 (tunable), power divided among multiple lines - lam_t = seq(4.68, 4.78, 0.01) * u.um - nline = len(lam_t) - power_t = 70e-3 * u.W / (c.c * c.h / lam) * u.ph / nline + if not isinstance(self.meta['laser_t_wave'], Iterable): + self.meta['laser_t_wave'] = [self.meta['laser_t_wave']] + lamc_t = self.meta["laser_t_wave"] * u.um + nline_t = len(lamc_t) + power_t = self.meta["laser_t_power"] * u.W / (c.c * c.h / lam) * u.ph / nline_t # Laser 3 (M band) - lamc_m = 5.26 * u.um - power_m = 20e-3 * u.W / (c.c * c.h / lamc_m) * u.ph + lamc_m = self.meta["laser_m_wave"] * u.um + power_m = self.meta["laser_m_power"] * u.W / (c.c * c.h / lamc_m) * u.ph + + # N-band lasers + if not isinstance(self.meta['laser_n_wave'], Iterable): + self.meta['laser_n_wave'] = [self.meta['laser_n_wave']] + if not isinstance(self.meta['laser_n_power'], Iterable): + self.meta['laser_n_power'] = [self.meta['laser_n_power']] + lamc_n = self.meta["laser_n_wave"] * u.um + power_n = self.meta["laser_n_power"] * u.W / (c.c * c.h / lamc_n) * u.ph + # Apply fibre transmission power_l *= self.fibre_trans power_t *= self.fibre_trans power_m *= self.fibre_trans + power_n *= self.fibre_trans sigma = 2 * dlam amp = 1/(sigma * np.sqrt(2 * np.pi)) line_l = Gaussian1D(amplitude=amp, mean=lamc_l, stddev=sigma) line_m = Gaussian1D(amplitude=amp, mean=lamc_m, stddev=sigma) - list_t = [Gaussian1D(amplitude=amp, mean=ll, stddev=sigma) for ll in lam_t] + list_t = [Gaussian1D(amplitude=amp, mean=ll, stddev=sigma) for ll in lamc_t] line_t = sum(list_t[1:], start=list_t[0]) + list_n = [Gaussian1D(amplitude=pp*amp, mean=ll, stddev=sigma) for (ll,pp) in + zip(lamc_n, power_n)] + line_n = sum(list_n[1:], start=list_n[0]) flux = ((power_l * line_l(lam) + power_m * line_m(lam) + power_t - * line_t(lam)) / (np.pi * self.d_is**2)) + * line_t(lam) + line_n(lam)) / (np.pi * self.d_is**2)) # emergent intensity from the IS output port intens = mult_is * flux / (np.pi * u.sr) diff --git a/scopesim/effects/mosaic_trace_list.py b/scopesim/effects/mosaic_trace_list.py index 102f7118a..8b23e3ad3 100644 --- a/scopesim/effects/mosaic_trace_list.py +++ b/scopesim/effects/mosaic_trace_list.py @@ -15,7 +15,7 @@ from .spectral_trace_list import SpectralTraceList from .spectral_trace_list_utils import SpectralTrace -from ..utils import get_logger, quantify, power_vector +from ..utils import get_logger, quantify, power_vector, from_currsys from ..optics.fov import FieldOfView from ..optics.fov_volume_list import FovVolumeList from ..detector import Detector @@ -33,7 +33,7 @@ class MosaicSpectralTraceList(SpectralTraceList): def __init__(self, **kwargs): super().__init__(**kwargs) - self.aplist = self._file["Aperture List"].data + self.aplist = Table(self._file["Aperture List"].data) # TODO: check units or normalise to arcsec self.view = np.array([(self.aplist["right"].max() - self.aplist["left"].min()), @@ -82,6 +82,9 @@ def apply_to(self, obj, **kwargs): # - map each 1D spectrum to detector/fov image = np.zeros((naxis2d, naxis1d), dtype=np.float32) + image_hdr = detwcs.to_header() + image_hdr["BUNIT"] = "ph s-1" + image_hdr.extend(det_header) for sptid, spt in tqdm(self.spectral_traces.items(), desc="Fiber traces", position=2): @@ -115,10 +118,8 @@ def apply_to(self, obj, **kwargs): detdisp = np.diff(detlam, prepend=detlam[0]) image[jfib,] += (spec(detlam) * detdisp).value - image_hdr = detwcs.to_header() - image_hdr["BUNIT"] = "ph s-1" - image_hdr.extend(det_header) obj.hdu = fits.ImageHDU(data=image, header=image_hdr) + return obj def make_spectral_traces(self): @@ -224,34 +225,93 @@ def gradient(self): return Transform1D(dcoeffs) -class MosaicCollapseSpectralTraces(MosaicSpectralTraceList): - """Collapse SpectralTraces to 1D spectrum. +class MosaicOutputFormat(MosaicSpectralTraceList): + """Determine output format for Mosaic MOS/mIFU data - .. versionadded:: 0.11.0 + format: str + "collapse1d": all fibres are summed into a single spectrum. + The output is a fits table with columns `wavelength` and + `spectrum`. (default for MOS modes) + "table": The output is a fits table with a row for each fibre. The + columns are `id`, `x`, `y`, `wavelength`, `spectrum`. + `x` and `y` give the sky position of the fiber. `wavelength` + and `spectrum` are arrays. (default for mIFU modes) + "image": The output is a (pseudo-)detector image where the + spectrum for each fibre occupies a row. + + .. versionadded:: PLACEHOLDER_NEXT_RELEASE_VERSION """ - required_keys = {"filename"} + required_keys = {"filename", "format"} z_order: ClassVar[tuple[int, ...]] = (899,) def apply_to(self, det, **kwargs): """Apply to detector readout.""" if not isinstance(det, Detector): + logger.warning("%s applied to object of type %s", + self.__class__.__name__, + type(det)) return det image = det._hdu.data detwcs = WCS(det._hdu.header, key="D") - spec = np.zeros(image.shape[1], dtype=np.float32) - for sptid, spt in tqdm(self.spectral_traces.items(), - desc="Fiber traces", position=2): - y_mm = spt.table["y"][0] - jfib = int(detwcs.all_world2pix(0, y_mm, 0)[1]) - spec += image[jfib,] - - x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] - lam = spt.x2lam(x_mm) - det._hdu = fits.BinTableHDU.from_columns([ - fits.Column(name="wavelength", format="D", array=lam, unit="um"), - fits.Column(name="spectrum", format="D", array=spec, unit="ADU"), - ]) + + output_format = from_currsys(self.meta["format"], self.cmds) + + if output_format == "collapse1d": + spec = np.zeros(image.shape[1], dtype=np.float32) + for sptid, spt in tqdm(self.spectral_traces.items(), + desc="Fiber traces", position=2): + x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] + lam = spt.x2lam(x_mm) + + y_mm = spt.table["y"][0] + jfib = int(detwcs.all_world2pix(0, y_mm, 0)[1]) + spec += image[jfib,] + + det._hdu = fits.BinTableHDU.from_columns([ + fits.Column(name="wavelength", format="D", array=lam, unit="um"), + fits.Column(name="spectrum", format="D", array=spec, unit="ADU"), + ]) + + elif output_format == "table": + ntrace = len(self.spectral_traces) + idarr = [] + xarr = np.zeros(ntrace, dtype=np.float32) + yarr = np.zeros(ntrace, dtype=np.float32) + specarr = [] + lamarr = [] + for i, (sptid, spt) in enumerate(tqdm( + self.spectral_traces.items(), + desc="Fiber traces", + position=2)): + x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] + lam = spt.x2lam(x_mm) + + y_mm = spt.table["y"][0] + jfib = int(detwcs.all_world2pix(0, y_mm, 0)[1]) + idarr.append(sptid) + xarr[i] = (self.aplist['left'][i] + self.aplist['right'][i]) / 2 + yarr[i] = (self.aplist['top'][i] + self.aplist['bottom'][i]) / 2 + specarr.append(image[jfib,]) + lamarr.append(lam) + + tab = Table(data=[idarr, xarr, yarr, lamarr, specarr], + names=["id", "x", "y", "wavelength", "spectrum"], + units=["", "arcsec", "arcsec", "um", "ADU"]) + det._hdu = fits.BinTableHDU(data=tab) + + elif output_format == "image": + pass + + else: + logger.error("Unknown output format: %s", output_format) return det + + + def __str__(self) -> str: + output_format = from_currsys(self.meta["format"], self.cmds) + msg = (f"{self.__class__.__name__} (\"{self.display_name}\"):" + f" \"{output_format}\"") + return msg diff --git a/scopesim/effects/psfs/discrete.py b/scopesim/effects/psfs/discrete.py index 78a1a42be..3e85adf9c 100644 --- a/scopesim/effects/psfs/discrete.py +++ b/scopesim/effects/psfs/discrete.py @@ -7,8 +7,7 @@ import numpy as np from tqdm.auto import tqdm from scipy.signal import convolve -from scipy.ndimage import zoom -from scipy.interpolate import griddata, RegularGridInterpolator +from scipy.interpolate import RectBivariateSpline, griddata from astropy import units as u from astropy.io import fits @@ -166,13 +165,17 @@ class FieldConstantPSF(DiscretePSF): .. versionchanged:: 0.10.0 PSF interpolation can now be limited to the central wavelength by - setting the "!OBS.interp_psf" keyword to False. + setting the "!SIM.psf.interp_psf" keyword to False. .. versionchanged:: 0.11.2 Fixed handling of background level and rounded edges to avoid visible "squares" in the image. + .. versionchanged:: PLACEHOLDER_NEXT_RELEASE_VERSION + + Interpolation spline order can now be set via "!SIM.psf.interp_order". + """ required_keys = {"filename"} @@ -197,9 +200,17 @@ def get_kernel(self, fov): if ext == self.current_layer_id: return self.kernel + try: + spline_order = from_currsys(self.meta["interp_order"], cmds=self.cmds) + except KeyError: + # Occurs in online notebook test, maybe temporary mismatch with irdb? + logger.warning("PSF parameter interp_order not found, using 1 (linear interpolation).") + spline_order = 1 + + if fov.hdu.header["NAXIS"] == 3: self.current_layer_id = ext - self.make_psf_cube(fov) + self.make_psf_cube(fov, spline_order) return self.kernel self.kernel = self._file[ext].data @@ -220,9 +231,8 @@ def get_kernel(self, fov): # rescaling kept inside loop to avoid rescaling for every fov pix_ratio = kernel_pixel_scale / fov_pixel_scale if abs(pix_ratio - 1) > self.meta["flux_accuracy"]: - spline_order = from_currsys( - "!SIM.computing.spline_order", cmds=self.cmds) self.kernel = _rescale_kernel(self.kernel, pix_ratio, + spline_order=spline_order, image_header=hdr) if ((fov.header["NAXIS1"] < hdr["NAXIS1"]) or @@ -232,8 +242,19 @@ def get_kernel(self, fov): return np.atleast_2d(self.kernel.squeeze()) - def make_psf_cube(self, fov): - """Create a wavelength-dependent psf cube.""" + def make_psf_cube(self, fov, spline_order=1): + """Create a wavelength-dependent psf cube. + + Parameters + ---------- + fov : FieldOfView + + spline_order : int + spline order for interpolation by ``RectBivariateSpline``. + Default is 1 (linear interpolation). The method uses the same order in both + directions (i.e. `kx = ky = spline_order`). + + """ # Some data from the fov nxfov, nyfov = fov.hdu.header["NAXIS1"], fov.hdu.header["NAXIS2"] fov_pixel_scale = fov.hdu.header["CDELT1"] @@ -245,7 +266,7 @@ def make_psf_cube(self, fov): + fov.hdu.header["CRVAL3"] ) - if not self.cmds.get("!OBS.interp_psf", True): + if not self.meta.get("interp_psf", True): lam = np.array([lam[len(lam)//2]]) # Some data from the psf file @@ -263,15 +284,10 @@ def make_psf_cube(self, fov): psfwcs = WCS(hdr) psf = self._file[ext].data psf = psf / psf.sum() # normalisation of the input psf - nxin, nyin = psf.shape + nyin, nxin = psf.shape - # We need linear interpolation to preserve positivity. Might think of - # more elaborate positivity-preserving schemes. - # An alternative would be to use method 'pchip', which uses monotonic - # splines to prevent overshooting (including to negative values) - ipsf = RegularGridInterpolator((np.arange(nyin), np.arange(nxin)), - psf, method='linear', bounds_error=False, - fill_value=0) + ipsf = RectBivariateSpline( + np.arange(nyin), np.arange(nxin), psf, kx=spline_order, ky=spline_order) # adapt the size of the output cube to the FOV's spatial shape psf_maxsize = self.cmds.get("!SIM.computing.psf_maxsize", np.nan) @@ -279,7 +295,7 @@ def make_psf_cube(self, fov): nxpsf = min(nxin, 2 * nxfov + 1, psf_maxsize) nypsf = min(nyin, 2 * nyfov + 1, psf_maxsize) - xcube, ycube = np.meshgrid(np.arange(nxpsf), np.arange(nypsf)) + xcube, ycube = np.meshgrid(np.arange(nxpsf), np.arange(nypsf), sparse=True) cubewcs = WCS(naxis=2) cubewcs.wcs.ctype = ["LINEAR", "LINEAR"] cubewcs.wcs.crval = [0., 0.] @@ -290,7 +306,7 @@ def make_psf_cube(self, fov): xworld, yworld = cubewcs.all_pix2world(xcube, ycube, 1) outcube = np.zeros((lam.shape[0], nypsf, nxpsf), dtype=np.float32) logger.info("Interpolating PSF onto %s cube", outcube.shape) - + logger.info("Interpolation order %d", spline_order) nlam = len(lam) for i, wave in tqdm(enumerate(lam), desc=" PSF slices", position=2, total=nlam, disable=(nlam <= 1)): @@ -298,11 +314,12 @@ def make_psf_cube(self, fov): psfwcs.wcs.cdelt = [psf_wave_pixscale, psf_wave_pixscale] xpsf, ypsf = psfwcs.all_world2pix(xworld, yworld, 0) - outcube[i,] = (ipsf( (ypsf.ravel(), xpsf.ravel()) ).reshape(outcube[i,].shape) + outcube[i,] = (ipsf( ypsf, xpsf, grid=False ).reshape(outcube[i,].shape) * fov_pixel_scale**2 / psf_wave_pixscale**2) # .squeeze() gets rid of any axes with length one self.kernel = outcube.reshape((lam.shape[0], nypsf, nxpsf)).squeeze() + # fits.writeto("test_psfcube.fits", data=self.kernel, overwrite=True) def plot(self): @@ -510,7 +527,7 @@ def _make_strehl_map_from_table(tbl, pixel_scale=1*u.arcsec): return map_hdu -def _rescale_kernel(image, scale_factor, method="linear", +def _rescale_kernel(image, scale_factor, spline_order=1, image_header=None): """Rescale `image` by `scale_factor` @@ -523,9 +540,10 @@ def _rescale_kernel(image, scale_factor, method="linear", ratio of input pixel size to output pixel size. Values larger than 1 zoom the image. - method : str - interpolation method to be passed to ``RegularGridInterpolator``. - Default is "linear" + spline_order : int + spline order for interpolation by ``RectBivariateSpline``. + Default is 1 (linear interpolation). The method uses the same order in both + directions (i.e. `kx = ky = spline_order`). image_header : astropy.fits.Header an optional image header with a WCS. If ``None``, a WCS is constructed @@ -537,13 +555,10 @@ def _rescale_kernel(image, scale_factor, method="linear", The output kernel size is always odd to avoid half-pixel shift when convolved with an image with ``mode="same"``. """ - nxin, nyin = image.shape - interp_img = RegularGridInterpolator( - (np.arange(nyin), np.arange(nxin)), - image, method=method, - bounds_error=False, - fill_value=0, - ) + nyin, nxin = image.shape + + interp_img = RectBivariateSpline( + np.arange(nyin), np.arange(nxin), image, kx=spline_order, ky=spline_order) # Make the output size odd so that the image remains centred (important # for PSF convolution). @@ -570,12 +585,13 @@ def _rescale_kernel(image, scale_factor, method="linear", outwcs.wcs.crpix = [(nxout + 1)/2, (nyout + 1)/2] outwcs.wcs.crval = [0., 0.] outwcs.wcs.cdelt = inwcs.wcs.cdelt / scale_factor - xout, yout = np.meshgrid(np.arange(nxout), np.arange(nyout)) + xout, yout = np.meshgrid(np.arange(nxout), np.arange(nyout), sparse=True) xworld, yworld = outwcs.all_pix2world(xout, yout, 0) xin, yin = inwcs.all_world2pix(xworld, yworld, 0) - outimage = interp_img( (yin.ravel(), xin.ravel()) ).reshape((nyout, nxout)) - logger.info("Interpolating PSF onto %s image", outimage.shape) + logger.info("Interpolating PSF onto %s image", (nyout, nxout)) + logger.info("Interpolation order %d", spline_order) + outimage = interp_img(yin, xin, grid=False).reshape((nyout, nxout)) outimage *= image.sum() / outimage.sum() return outimage diff --git a/scopesim/effects/psfs/psf_base.py b/scopesim/effects/psfs/psf_base.py index 11a1b7cc3..4f617c34c 100644 --- a/scopesim/effects/psfs/psf_base.py +++ b/scopesim/effects/psfs/psf_base.py @@ -4,6 +4,7 @@ from typing import ClassVar import numpy as np +from numpy.typing import ArrayLike, NDArray from scipy.signal import convolve from scipy.ndimage import rotate from astropy import units as u @@ -57,76 +58,109 @@ def __init__(self, **kwargs): self.meta = from_currsys(self.meta, self.cmds) self.convolution_classes = (FieldOfView, ImagePlane) + def __call__(self, data: ArrayLike, kernel: ArrayLike) -> NDArray: + # subtract background level before convolving, re-add afterwards + bkg_level = get_bkg_level(data, self.meta["bkg_width"]) + + if data.ndim == 3: + bkg_level = bkg_level[:, None, None] + + image = np.asarray(data - bkg_level) + + # Shortcut for flat images, which wouldn't change with convolution + if image.sum() == 0.: + logger.debug( + "%s: uniform field, convolution skipped", self.display_name) + return data # return unchanged + + mode = from_currsys(self.meta["convolve_mode"], self.cmds) + + match image.ndim, kernel.ndim: + case 2, 2: + return convolve(image, kernel, mode=mode) + bkg_level + + case 3, 2: + kernel = kernel[None, ...] + return convolve(image, kernel, mode=mode) + bkg_level + + case 3, 3: + if mode != "same": + logger.warning( + "cube-cube convolution assumes mode='same', but mode " + "is %s, result may be inconsistent.", mode) + + convolved = np.zeros(data.shape) # assumes mode="same" + for iplane in range(data.shape[0]): + convolved[iplane,] = convolve( + image[iplane,], + kernel[iplane,], + mode=mode, + ) + return convolved + bkg_level + + case _: + raise ValueError("Image and Kernel shape mismatch.") + + def _apply_to_fvl(self, fvl: FovVolumeList) -> None: + logger.debug("Apply %s, FoV setup", self.display_name) + + if len(self._waveset) == 0: + return + + waveset_edges = 0.5 * (self._waveset[:-1] + self._waveset[1:]) + fvl.split("wave", quantify(waveset_edges, u.um).value) + + def _apply_convolution(self, obj) -> None: + logger.debug("Apply %s, convolution", self.display_name) + + if ((not hasattr(obj, "fields") or len(obj.fields) == 0) + and (obj.hdu is None)): + return + + kernel = self.get_kernel(obj).astype(float) + + # This doesn't work because of a "Delta PSF" in some mocks... + # if kernel.size == 1: # only 1 pixel + # raise ValueError("Cannot convolve single pixel PSF.") + + # apply rotational blur for field-tracking observations + rot_blur_angle = self.meta["rotational_blur_angle"] + if abs(rot_blur_angle << u.deg) > 0*u.deg: + # makes a copy of kernel + kernel = rotational_blur(kernel, rot_blur_angle) + + # Round the edges of kernels to avoid square stars + if self.meta.get("rounded_edges", False) and kernel.ndim == 2: + kernel = self._round_kernel_edges(kernel) + + # normalise psf kernel KERNEL SHOULD BE normalised within get_kernel() + # if from_currsys(self.meta["normalise_kernel"], self.cmds): + # kernel /= np.sum(kernel) + # kernel[kernel < 0.] = 0. + + orig_shape = obj.hdu.data.shape + + logger.debug("PSF convolution start") + obj.hdu.data = self(obj.hdu.data.astype(float), kernel) + logger.debug("PSF convolution done") + + # TODO: careful with which dimensions mean what + d_x = obj.hdu.data.shape[-1] - orig_shape[-1] + d_y = obj.hdu.data.shape[-2] - orig_shape[-2] + for wcsid in ["", "D"]: + if "CRPIX1" + wcsid in obj.hdu.header: + obj.hdu.header["CRPIX1" + wcsid] += d_x / 2 + obj.hdu.header["CRPIX2" + wcsid] += d_y / 2 + def apply_to(self, obj, **kwargs): """Apply the PSF.""" # 1. During setup of the FieldOfViews if isinstance(obj, FovVolumeList) and self._waveset is not None: - logger.debug("Executing %s, FoV setup", self.meta['name']) - waveset = self._waveset - if len(waveset) != 0: - waveset_edges = 0.5 * (waveset[:-1] + waveset[1:]) - obj.split("wave", quantify(waveset_edges, u.um).value) + self._apply_to_fvl(obj) # 2. During observe: convolution elif isinstance(obj, self.convolution_classes): - logger.debug("Executing %s, convolution", self.meta['name']) - if ((hasattr(obj, "fields") and len(obj.fields) > 0) or - (obj.hdu is not None)): - kernel = self.get_kernel(obj).astype(float) - - # This doesn't work because of a "Delta PSF" in some mocks... - # if kernel.size == 1: # only 1 pixel - # raise ValueError("Cannot convolve single pixel PSF.") - - # apply rotational blur for field-tracking observations - rot_blur_angle = self.meta["rotational_blur_angle"] - if abs(rot_blur_angle << u.deg) > 0*u.deg: - # makes a copy of kernel - kernel = rotational_blur(kernel, rot_blur_angle) - - # Round the edges of kernels so that the silly square stars - # don't appear anymore - if self.meta.get("rounded_edges", False) and kernel.ndim == 2: - kernel = self._round_kernel_edges(kernel) - - # normalise psf kernel KERNEL SHOULD BE normalised within get_kernel() - # if from_currsys(self.meta["normalise_kernel"], self.cmds): - # kernel /= np.sum(kernel) - # kernel[kernel < 0.] = 0. - - image = obj.hdu.data.astype(float) - - # subtract background level before convolving, re-add afterwards - bkg_level = get_bkg_level(image, self.meta["bkg_width"]) - - # do the convolution - mode = from_currsys(self.meta["convolve_mode"], self.cmds) - - logger.debug("PSF convolution start") - if image.ndim == 2 and kernel.ndim == 2: - new_image = convolve(image - bkg_level, kernel, mode=mode) - elif image.ndim == 3 and kernel.ndim == 2: - kernel = kernel[None, :, :] - bkg_level = bkg_level[:, None, None] - new_image = convolve(image - bkg_level, kernel, mode=mode) - elif image.ndim == 3 and kernel.ndim == 3: - bkg_level = bkg_level[:, None, None] - new_image = np.zeros(image.shape) # assumes mode="same" - for iplane in range(image.shape[0]): - new_image[iplane,] = convolve( - image[iplane,] - bkg_level[iplane,], - kernel[iplane,], mode=mode) - - obj.hdu.data = new_image + bkg_level - logger.debug("PSF convolution done") - - # TODO: careful with which dimensions mean what - d_x = new_image.shape[-1] - image.shape[-1] - d_y = new_image.shape[-2] - image.shape[-2] - for wcsid in ["", "D"]: - if "CRPIX1" + wcsid in obj.hdu.header: - obj.hdu.header["CRPIX1" + wcsid] += d_x / 2 - obj.hdu.header["CRPIX2" + wcsid] += d_y / 2 + self._apply_convolution(obj) return obj @@ -137,7 +171,7 @@ def get_kernel(self, obj): return self.kernel @staticmethod - def _round_kernel_edges(kernel: np.ndarray) -> np.ndarray: + def _round_kernel_edges(kernel: ArrayLike) -> NDArray: x, y = np.array(kernel.shape) // 2 threshold = min(kernel[x, 0], kernel[x, -1], kernel[0, y], kernel[-1, y]) @@ -172,7 +206,7 @@ def rotational_blur(image, angle: u.Quantity[u.deg]): Returns ------- - image_rot : np.ndarray + image_rot : NDArray Blurred image """ diff --git a/scopesim/effects/spectral_trace_list.py b/scopesim/effects/spectral_trace_list.py index d455fc088..f0583baf9 100644 --- a/scopesim/effects/spectral_trace_list.py +++ b/scopesim/effects/spectral_trace_list.py @@ -14,13 +14,13 @@ from astropy.io import fits from astropy.table import Table -from .effects import Effect -from .ter_curves import FilterCurve -from .spectral_trace_list_utils import SpectralTrace, make_image_interpolations +from ..utils import from_currsys, check_keys, figure_factory, get_logger from ..optics.image_plane_utils import header_from_list_of_xy from ..optics.fov import FieldOfView from ..optics.fov_volume_list import FovVolumeList -from ..utils import from_currsys, check_keys, figure_factory, get_logger +from .effects import Effect +from .ter_curves import FilterCurve +from .spectral_trace_list_utils import SpectralTrace, make_image_interpolations logger = get_logger(__name__) @@ -257,7 +257,11 @@ def apply_to(self, obj, **kwargs): self.update_meta() spt = self.spectral_traces[obj.trace_id] - obj.hdu = spt.map_spectra_to_focal_plane(obj) + try: + # If footprint is outside FOV, this will assign None to the hdu + obj.hdu = spt.map_spectra_to_focal_plane(obj) + except ValueError as err: # xlim_mm is None + logger.error(err) logger.debug("%s done", self.display_name) return obj @@ -291,9 +295,6 @@ def rectify_traces(self, hdulist, xi_min=None, xi_max=None, interps=None, This method creates an HDU list with one extension per spectral trace, i.e. it essentially treats all traces independently. - For the case of an IFU where the traces correspond to spatial - slices for the same wavelength range, use method `rectify_cube` - (not yet implemented). Parameters ---------- @@ -361,23 +362,30 @@ def rectify_traces(self, hdulist, xi_min=None, xi_max=None, interps=None, for i, trace_id in tqdm(enumerate(self.spectral_traces, start=1), desc=" Traces", total=len(self.spectral_traces)): - hdu = self[trace_id].rectify(hdulist, - interps=interps, - bin_width=bin_width, - xi_min=xi_min, xi_max=xi_max, - wave_min=wave_min, wave_max=wave_max) - if hdu is not None: # ..todo: rectify does not do that yet - outhdul.append(hdu) - outhdul[0].header[f"EXTNAME{i}"] = trace_id + try: + hdu = self[trace_id].rectify( + hdulist, + interps=interps, + bin_width=bin_width, + xi_min=xi_min, + xi_max=xi_max, + wave_min=wave_min, + wave_max=wave_max, + ) + except (KeyError, ValueError) as err: + logger.error(err) + continue + + if hdu is None: # TODO: rectify does not do that yet + continue + + outhdul.append(hdu) + outhdul[0].header[f"EXTNAME{i}"] = trace_id outhdul[0].header.update(inhdul[0].header) return outhdul - def rectify_cube(self, hdulist): - """Rectify traces and combine into a cube.""" - raise NotImplementedError() - def plot(self, wave_min=None, wave_max=None, axes=None, **kwargs): """Plot every spectral trace in the spectral trace list. diff --git a/scopesim/effects/spectral_trace_list_utils.py b/scopesim/effects/spectral_trace_list_utils.py index 7541f9cbf..721753371 100644 --- a/scopesim/effects/spectral_trace_list_utils.py +++ b/scopesim/effects/spectral_trace_list_utils.py @@ -24,8 +24,14 @@ from astropy.wcs import WCS from astropy.modeling.models import Polynomial2D -from ..utils import (power_vector, quantify, from_currsys, close_loop, - figure_factory, get_logger) +from ..utils import ( + power_vector, + quantify, + from_currsys, + close_loop, + figure_factory, + get_logger, +) logger = get_logger(__name__) @@ -87,6 +93,7 @@ def __init__(self, trace_tbl, cmds=None, **kwargs): # Declaration of other attributes self._xilamimg = None self.dlam_per_pix = None + self.fit_inverse = True @property def trace_id(self): @@ -153,7 +160,8 @@ def compute_interpolation_functions(self): logger.info( "Dispersion axis determined to be %s", self.dispersion_axis) - def map_spectra_to_focal_plane(self, fov): + # TODO: add typing to fov: FieldOfView, but that creates a circular import + def map_spectra_to_focal_plane(self, fov) -> fits.ImageHDU: """ Apply the spectral trace mapping to a spectral cube. @@ -170,12 +178,15 @@ def map_spectra_to_focal_plane(self, fov): wave_max = fov.meta["wave_max"].value # [um] xi_min = fov.meta["xi_min"].value # [arcsec] xi_max = fov.meta["xi_max"].value # [arcsec] - xlim_mm, ylim_mm = self.footprint(wave_min=wave_min, wave_max=wave_max, - xi_min=xi_min, xi_max=xi_max) + xlim_mm, ylim_mm = self.footprint( + wave_min=wave_min, + wave_max=wave_max, + xi_min=xi_min, + xi_max=xi_max, + ) if xlim_mm is None: - logger.warning("xlim_mm is None") - return None + raise ValueError("xlim_mm is None") fov_header = fov.header det_header = fov.detector_header @@ -234,12 +245,10 @@ def map_spectra_to_focal_plane(self, fov): xilam_wcs = xilam.wcs # focal-plane coordinate images - ximg_fpa, yimg_fpa = np.meshgrid(np.linspace(xmin_mm, xmax_mm, - sub_naxis1, - dtype=np.float32), - np.linspace(ymin_mm, ymax_mm, - sub_naxis2, - dtype=np.float32)) + ximg_fpa, yimg_fpa = np.meshgrid( + np.linspace(xmin_mm, xmax_mm, sub_naxis1, dtype=np.float32), + np.linspace(ymin_mm, ymax_mm, sub_naxis2, dtype=np.float32), + ) # Image mapping (xi, lambda) on the focal plane xi_fpa = self.xy2xi(ximg_fpa, yimg_fpa).astype(np.float32) @@ -275,8 +284,9 @@ def map_spectra_to_focal_plane(self, fov): # Scale to ph / s / pixel dlam_by_dx, dlam_by_dy = self.xy2lam.gradient() - dlam_per_pix = pixsize * np.sqrt(dlam_by_dx(ximg_fpa, yimg_fpa)**2 + - dlam_by_dy(ximg_fpa, yimg_fpa)**2) + dlam_per_pix = pixsize * np.sqrt( + dlam_by_dx(ximg_fpa, yimg_fpa)**2 + + dlam_by_dy(ximg_fpa, yimg_fpa)**2) image *= pixscale * dlam_per_pix # [arcsec/pix] * [um/pix] # img_header = sub_wcs.to_header() @@ -288,15 +298,35 @@ def map_spectra_to_focal_plane(self, fov): img_header["YMAX"] = ymax if np.any(image < 0): - logger.warning("map_spectra_to_focal_plane: %d negative pixels", - np.sum(image < 0)) + logger.warning( + "map_spectra_to_focal_plane: %d negative pixels", + np.sum(image < 0)) image_hdu = fits.ImageHDU(header=img_header, data=image) return image_hdu - def rectify(self, hdulist, interps=None, wcs=None, **kwargs): + def rectify( + self, + hdulist: fits.HDUList, + interps: list | None = None, + wcs: WCS | None = None, + fit_inverse: bool = True, + bin_width: float | None = None, + wave_min: float | None = None, + wave_max: float | None = None, + xi_min: float | None = None, + xi_max: float | None = None, + ) -> fits.ImageHDU | None: """Create 2D spectrum for a trace. + .. todo: Consider turning `interps` into a dict keyed on extname. + + .. todo: Proper typing on `interps`. + + .. todo: Rewrite and expand docstring (FH). + + .. todo: Consider using quantities for float parameters. + Parameters ---------- hdulist : HDUList @@ -322,21 +352,21 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): Spatial limits of the slit on the sky. This should be taken from the header of the hdulist, but this is not yet provided by scopesim """ - wave_min = kwargs.get("wave_min", - self.wave_min) - wave_max = kwargs.get("wave_max", - self.wave_max) + # Note: fit_inverse used to be self.fit_inverse, but wasn't used + # outside this function. + wave_min = wave_min if wave_min is not None else self.wave_min + wave_max = wave_max if wave_max is not None else self.wave_max if wave_max < self.wave_min or wave_min > self.wave_max: logger.debug(" Outside filter range") return None + wave_min = max(wave_min, self.wave_min) wave_max = min(wave_max, self.wave_max) logger.info("Rectifying %s (%.02f .. %.02f um)", self.trace_id, wave_min, wave_max) # bin_width is taken as the minimum dispersion of the trace - # ..todo: if wcs is given take bin width from cdelt1 - bin_width = kwargs.get("bin_width", None) + # TODO: if wcs is given take bin width from cdelt1 if bin_width is None: self._set_dispersion(wave_min, wave_max) bin_width = np.abs(self.dlam_per_pix.y).min() @@ -345,20 +375,23 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): pixscale = from_currsys(self.meta["pixel_scale"], self.cmds) # Temporary solution to get slit length - xi_min = kwargs.get("xi_min", None) if xi_min is None: try: xi_min = hdulist[0].header["HIERARCH INS SLIT XIMIN"] - except KeyError: - logger.error("xi_min not found") - return None - xi_max = kwargs.get("xi_max", None) + except KeyError as err: + raise KeyError( + "xi_min must be in header as 'INS SLIT XIMIN' or provided " + "directly as an argument." + ) from err + if xi_max is None: try: xi_max = hdulist[0].header["HIERARCH INS SLIT XIMAX"] - except KeyError: - logger.error("xi_max not found") - return None + except KeyError as err: + raise KeyError( + "xi_max must be in header as 'INS SLIT XIMAX' or provided " + "directly as an argument." + ) from err if wcs is None: wcs = WCS(naxis=2) @@ -379,16 +412,33 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): interps = make_image_interpolations(hdulist, kx=1, ky=1) # Create Xi, Lam images (do I need Iarr and Jarr or can I build Xi, Lam directly?) - Iarr, Jarr = np.meshgrid(np.arange(nlam, dtype=np.float32), - np.arange(nxi, dtype=np.float32)) + Iarr, Jarr = np.meshgrid( + np.arange(nlam, dtype=np.float32), + np.arange(nxi, dtype=np.float32), + ) Lam, Xi = wcs.all_pix2world(Iarr, Jarr, 0) # Make sure that we do have microns Lam = Lam * u.Unit(wcs.wcs.cunit[0]).to(u.um) + # TODO: Replace self.xilam2x and self.xilam2y by fitted + # functions fit_xilam2x and fit_xilam2y, which need + # to be determined by mapping a grid of (xi, lam) to (x,y) + # and back to (xip, lamp); then fit (x,y)(xip, lamp). + if fit_inverse: + fit_xilam2x, fit_xilam2y = self._fit_transform( + xi_min, + xi_max, + wave_min, + wave_max, + ) + else: + logger.debug("Using matrix transform") + fit_xilam2x, fit_xilam2y = self.xilam2x, self.xilam2y + # Convert Xi, Lam to focal plane units - Xarr = self.xilam2x(Xi, Lam) - Yarr = self.xilam2y(Xi, Lam) + Xarr = fit_xilam2x(Xi, Lam) + Yarr = fit_xilam2y(Xi, Lam) rect_spec = np.zeros_like(Xarr, dtype=np.float32) @@ -413,6 +463,33 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): header["EXTNAME"] = self.trace_id return fits.ImageHDU(data=rect_spec, header=header) + def _fit_transform(self, ximin, ximax, wmin, wmax): + """Fit inverses to self.xy2xi and self.xy2lam + + The method replaces self.xilam2x and self.xilam2y by fitted + functions fit_xilam2x and fit_xilam2y, determined by mapping a grid of + (xi, lam) to (x,y) and back to (xip, lamp); then fit (x,y)(xip, lamp). + """ + + # Build input (xi, lam) grid + xi = np.linspace(ximin, ximax, 51) + lam = np.linspace(wmin, wmax, 201) + + # Do a full round using the matrix transforms + x2d = self.xilam2x(xi, lam, grid=True) + y2d = self.xilam2y(xi, lam, grid=True) + xip = self.xy2xi(x2d, y2d, grid=False) + lamp = self.xy2lam(x2d, y2d, grid=False) + + # Determine the fits + pinit_x = Polynomial2D(degree=4) + pinit_y = Polynomial2D(degree=4) + fitter = fitting.LinearLSQFitter() + xilam2x = fitter(pinit_x, xip, lamp, x2d) + xilam2y = fitter(pinit_y, xip, lamp, y2d) + + return xilam2x, xilam2y + def footprint(self, wave_min=None, wave_max=None, xi_min=None, xi_max=None): """ Return corners of rectangle enclosing spectral trace. @@ -487,14 +564,18 @@ def footprint(self, wave_min=None, wave_max=None, xi_min=None, xi_max=None): # Map the edges of xi/lam to the focal plance n_edge = 512 - wave_edge = np.concatenate((np.linspace(wave_min, wave_max, n_edge), - [wave_max] * n_edge, - np.linspace(wave_min, wave_max, n_edge), - [wave_min] * n_edge)) - xi_edge = np.concatenate(([xi_min] * n_edge, - np.linspace(xi_min, xi_max, n_edge), - [xi_max] * n_edge, - np.linspace(xi_min, xi_max, n_edge))) + wave_edge = np.concatenate(( + np.linspace(wave_min, wave_max, n_edge), + [wave_max] * n_edge, + np.linspace(wave_min, wave_max, n_edge), + [wave_min] * n_edge, + )) + xi_edge = np.concatenate(( + [xi_min] * n_edge, + np.linspace(xi_min, xi_max, n_edge), + [xi_max] * n_edge, + np.linspace(xi_min, xi_max, n_edge), + )) x_edge = self.xilam2x(xi_edge, wave_edge) y_edge = self.xilam2y(xi_edge, wave_edge) @@ -586,7 +667,7 @@ def plot(self, wave_min=None, wave_max=None, xi_min=None, xi_max=None, *, if plot_ctrlpnts: axes.plot(x, y, "o", c=c) - if plot_outline: + if plot_outline or plot_trace_id: blue_end = self.table[mask][w == w.min()] red_end = self.table[mask][w == w.max()] blue_end.sort(sname) @@ -594,6 +675,8 @@ def plot(self, wave_min=None, wave_max=None, xi_min=None, xi_max=None, *, corners = vstack([blue_end[[0, -1]][xname, yname], red_end[[-1, 0]][xname, yname], blue_end[0][xname, yname]]) + + if plot_outline: axes.plot(corners[xname], corners[yname], c=c) if plot_trace_id: @@ -725,7 +808,7 @@ def __init__(self, fov, dlam_per_pix): self.wcsa.wcs.cdelt = [d_lam, 1./n_xi] self.wcsa.wcs.ctype = ["LINEAR", "LINEAR"] self.wcsa.wcs.cname = ["WAVELEN", "SLITPOS"] - self.wcs.wcs.cunit = ["um", ""] + self.wcsa.wcs.cunit = ["um", ""] self.xi = self.wcs.all_pix2world(self.lam[0], np.arange(n_xi), 0)[1] self.npix_xi = n_xi @@ -806,7 +889,8 @@ def __call__(self, x, y, grid=False, **kwargs): this case, x and y must be of the same length). Functions `pretransform_x`, `pretransform_y` and `posttransform` - can be supplied to override the instance values. + can be supplied to override the instance values for this call only; + the instance itself is not modified. Parameters ---------- @@ -821,12 +905,16 @@ def __call__(self, x, y, grid=False, **kwargs): in x and y. When grid=False, a vector. In this case, x and y must have the same length. """ + # Per-call overrides must not modify the instance + pretransform_x = self.pretransform_x + pretransform_y = self.pretransform_y + posttransform = self.posttransform if "pretransform_x" in kwargs: - self.pretransform_x = self._repackage(kwargs["pretransform_x"]) + pretransform_x = self._repackage(kwargs["pretransform_x"]) if "pretransform_y" in kwargs: - self.pretransform_y = self._repackage(kwargs["pretransform_y"]) + pretransform_y = self._repackage(kwargs["pretransform_y"]) if "posttransform" in kwargs: - self.posttransform = self._repackage(kwargs["posttransform"]) + posttransform = self._repackage(kwargs["posttransform"]) x = np.array(x) y = np.array(y) @@ -837,10 +925,10 @@ def __call__(self, x, y, grid=False, **kwargs): "is False") # Apply pre transforms - if self.pretransform_x is not None: - x = self.pretransform_x[0](x, **self.pretransform_x[1]) - if self.pretransform_y is not None: - y = self.pretransform_y[0](y, **self.pretransform_y[1]) + if pretransform_x is not None: + x = pretransform_x[0](x, **pretransform_x[1]) + if pretransform_y is not None: + y = pretransform_y[0](y, **pretransform_y[1]) xvec = power_vector(x.flatten(), self.nx - 1) yvec = power_vector(y.flatten(), self.ny - 1) @@ -860,8 +948,8 @@ def __call__(self, x, y, grid=False, **kwargs): result = result.reshape(orig_shape) # Apply posttransform - if self.posttransform is not None: - result = self.posttransform[0](result, **self.posttransform[1]) + if posttransform is not None: + result = posttransform[0](result, **posttransform[1]) return result diff --git a/scopesim/effects/ter_curves.py b/scopesim/effects/ter_curves.py index c653fd73c..4d5e1f8b3 100644 --- a/scopesim/effects/ter_curves.py +++ b/scopesim/effects/ter_curves.py @@ -286,7 +286,8 @@ class AtmoLibraryTERCurve(AtmosphericTERCurve): extension 3 etc. Tables with columns `transmission` and `emission`. - Currently the curves are distinguished by a single parameter (`pwv`). + Currently the curves are distinguished by a single parameter, whose name + is specified by `parameter` (cf. the examples below). .. versionadded:: 0.11.1 @@ -298,12 +299,14 @@ class AtmoLibraryTERCurve(AtmosphericTERCurve): class: AtmoLibraryTERCurve kwargs: filename: "!ATMO.spectrum.filename + parameter: "pwv" pwv: 10. - name: atmosphere class: AtmoLibraryTERCurve kwargs: - pwv: 25. + parameter: "relH" + relH: 0.25 remote_filename: "!ATMO.spectrum.filename" The location of a downloaded file is provided by `.meta['filename']`. @@ -318,9 +321,9 @@ def __init__(self, cmds=None, **kwargs): remote_filename = from_currsys(kwargs["remote_filename"], cmds) kwargs["filename"] = self._download_library(remote_filename) - self.param = 'pwv' super().__init__(cmds=cmds, **kwargs) self.meta.update(kwargs) + self.param = self.meta['parameter'] self.load_table_from_library() def update(self, **kwargs): @@ -343,8 +346,7 @@ def _download_library(fname: str) -> str: def load_table_from_library(self): """Load the appropriate library extension based on parameter value.""" - param = 'pwv' - + param = self.param self.value = from_currsys(self.meta[param], self.cmds) self.ext_data = self._file[0].header["EDATA"] self.ext_cat = self._file[0].header["ECAT"] @@ -367,10 +369,12 @@ def load_table_from_library(self): tbl.add_column(wavelength, index=0) tbl.meta["wavelength_unit"] = tbl["wavelength"].unit - tbl.meta["emission_unit"] = tbl["emission"].unit + if "emission" in tbl.colnames: + tbl.meta["emission_unit"] = tbl["emission"].unit self.surface.table = tbl self.surface.meta.update(tbl.meta) + self.meta.update(tbl.meta) def __str__(self) -> str: """Return str(self).""" diff --git a/scopesim/optics/image_plane.py b/scopesim/optics/image_plane.py index 794c03fbb..e2b101f48 100644 --- a/scopesim/optics/image_plane.py +++ b/scopesim/optics/image_plane.py @@ -77,38 +77,23 @@ def __init__(self, header, cmds=None, **kwargs): self._sky_wcs = self._get_wcs(header, " ") logger.debug("sky %s", self._sky_wcs) - def add(self, hdus_or_tables, sub_pixel=None, spline_order=None, + def add(self, hdus, sub_pixel=None, spline_order=None, wcs_suffix=""): """ - Add a projection of an image or table files to the canvas. - - .. note:: - If a Table is provided, it must include the following columns: - `x_mm`, `y_mm`, and `flux`. - - Units for the columns should be provided in the - .unit attribute or as an entry in the table's meta - dictionary using this syntax: -
.meta["_unit"] = . - - For example:: - - tbl["x"].unit = u.arcsec # or - tbl.meta[x_unit"] = "deg" - - If no units are given, default units will be assumed. These are: - - - `x`, `y`: `arcsec` - - `flux` : `ph / s / pix` + Add a projection of an image to the canvas. .. versionchanged:: 0.10.0 Adding a table directly to the ImagePlane is deprecated. Use FOV to add tables and image HDUs together before adding them to here. + .. versionchanged:: 0.12.0 + + Support for adding tables fully removed. See note above. + Parameters ---------- - hdus_or_tables : `fits.ImageHDU` or `astropy.Table` + hdus : `fits.ImageHDU` or list thereof The input to be projected onto the image plane. See above. sub_pixel : bool, optional @@ -129,30 +114,25 @@ def add(self, hdus_or_tables, sub_pixel=None, spline_order=None, if spline_order is None: spline_order = from_currsys("!SIM.computing.spline_order", self.cmds) - if isinstance(hdus_or_tables, (list, tuple)): - logger.debug("Adding multiple HDUs to ImagePlane.") - for hdu_or_table in hdus_or_tables: - self.add(hdu_or_table, sub_pixel, spline_order, wcs_suffix) + if isinstance(hdus, (list, tuple)): + logger.info("Adding multiple HDUs to ImagePlane.") + for hdu in hdus: + self.add(hdu, sub_pixel, spline_order, wcs_suffix) else: - if isinstance(hdus_or_tables, Table): - warn("Adding a table directly to the ImagePlane is deprecated " - "since v0.10.0. Passing a table to ImagePlane.add() will " - "raise an error from version 0.12 onwards. Use FOV to " - "add tables and image HDUs together before.", - DeprecationWarning, stacklevel=2) - self.hdu = add_table_to_imagehdu(hdus_or_tables, self.hdu, - sub_pixel, wcs_suffix) - elif isinstance(hdus_or_tables, fits.ImageHDU): - logger.debug("Adding HDU with shape %d to ImagePlane.", - hdus_or_tables.data.shape) - self.hdu = add_imagehdu_to_imagehdu(hdus_or_tables, self.hdu, - spline_order, wcs_suffix) - if (img_bunit := hdus_or_tables.header.get("BUNIT")) is not None: - if img_bunit != (imp_bunit := self.hdu.header["BUNIT"]): - logger.warning("Added mismatched BUNIT %s to %s.", - img_bunit, imp_bunit) - else: - logger.warning("No BUNIT found in added HDU.") + if not isinstance(hdus, fits.ImageHDU): + logger.error("tried to add %s", type(hdus)) + raise TypeError("Only ImageHDUs may be added to ImagePlane.") + + logger.debug("Adding HDU with shape %d to ImagePlane.", + hdus.data.shape) + self.hdu = add_imagehdu_to_imagehdu( + hdus, self.hdu, spline_order, wcs_suffix) + if (img_bunit := hdus.header.get("BUNIT")) is not None: + if img_bunit != (imp_bunit := self.hdu.header["BUNIT"]): + logger.warning("Added mismatched BUNIT %s to %s.", + img_bunit, imp_bunit) + else: + logger.info("No BUNIT found in added HDU.") @property def header(self): diff --git a/scopesim/optics/image_plane_utils.py b/scopesim/optics/image_plane_utils.py index 6de378b6c..62f717315 100644 --- a/scopesim/optics/image_plane_utils.py +++ b/scopesim/optics/image_plane_utils.py @@ -4,6 +4,7 @@ from collections.abc import Iterable import numpy as np +from numpy.typing import ArrayLike, NDArray from astropy import units as u from astropy.wcs import WCS, find_all_wcs from astropy.io import fits @@ -154,9 +155,11 @@ def _make_bounding_header_for_tables(*tables, pixel_scale=1*u.arcsec): return hdr -def create_wcs_from_points(points: np.ndarray, - pixel_scale: float, - wcs_suffix: str = "") -> tuple[WCS, np.ndarray]: +def create_wcs_from_points( + points: ArrayLike, + pixel_scale: float, + wcs_suffix: str = "", +) -> tuple[WCS, NDArray]: """ Create `astropy.wcs.WCS` instance that fits all points inside. @@ -434,7 +437,12 @@ def overlay_image(small_im, big_im, coords, mask=None, sub_pixel=False): coords = np.array([*coords, (big_im.shape[0] - 1) / 2]) # FIXME: this would not be necessary if we used WCS instead of manual 2pix - coords = np.ceil(np.asarray(coords).round(10)).astype(int) + # Round to 1e-4 pix before ceil: WCS deg-space round-trips leave + # float dust of order 1e-8..1e-6 pix on integer-valued coords, which + # ceil would amplify to a full-pixel shift; genuine sub-pixel intent + # cannot be finer than 0.5 pix here (sub_pixel is not implemented), + # and the half-integer even-shape convention is preserved exactly. + coords = np.ceil(np.asarray(coords).round(4)).astype(np.intp) idx = coords.astype(int)[::-1] - np.array(small_im.shape) // 2 # Image ranges @@ -534,13 +542,62 @@ def rescale_imagehdu(imagehdu: fits.ImageHDU, pixel_scale: float | u.Quantity, sum_orig = np.sum(imagehdu.data) # Perform the rescaling. Axes need to be inverted because python. - new_im = ndi.zoom(imagehdu.data, zoom[::-1], order=spline_order) + zoom_np = zoom[::-1] + data = imagehdu.data + in_shape = np.array(data.shape) # pre-zoom, for the realised-zoom CRPIX + + # ndi.zoom point-samples the input on the output lattice; for zoom < 1 that + # DECIMATES rather than integrates, so a source narrower than the sample + # spacing can be aliased away partially or (on an unlucky grid phase) + # entirely -- silently, since the conserve_flux guard below skips a zeroed + # array. Downsample each such axis with an exact area-weighted rebin (the + # cumulative sum evaluated at the output bin edges) instead. Unlike a + # smoothing prefilter this integrates without SPREADING flux beyond the + # output cell, so a compact source cannot leak into wings that a smaller + # downstream FOV would clip (which would defeat conserve_flux). It conserves + # flux to machine precision by construction and handles non-integer ratios. + # + # Gated on spline_order > 0: order == 0 is nearest-neighbour resampling of + # categorical data (masks, strehl-region label maps), where integrating / + # averaging is meaningless and would synthesise phantom label values -- those + # keep the stock ndi.zoom path. Upsampled axes also use stock ndi.zoom. + if spline_order > 0 and (zoom_np < 1).any(): + out = np.asarray(data, dtype=float) + for _ax in range(out.ndim): + _n_in = out.shape[_ax] + _n_out = max(int(round(_n_in * zoom_np[_ax])), 1) + if _n_out == _n_in: + continue + if _n_out < _n_in: + _a = np.moveaxis(out, _ax, 0) + _csum = np.empty((_n_in + 1,) + _a.shape[1:], dtype=float) + _csum[0] = 0.0 + np.cumsum(_a, axis=0, out=_csum[1:]) + _edges = np.linspace(0.0, _n_in, _n_out + 1) + _lo = np.floor(_edges).astype(np.intp) + _frac = _edges - _lo + _loc = np.clip(_lo, 0, _n_in) + _hic = np.clip(_lo + 1, 0, _n_in) + _fr = _frac[(...,) + (None,) * (_a.ndim - 1)] + _vals = _csum[_loc] + (_csum[_hic] - _csum[_loc]) * _fr + out = np.moveaxis(np.diff(_vals, axis=0), 0, _ax) + else: + _zf = np.ones(out.ndim) + _zf[_ax] = _n_out / _n_in + out = ndi.zoom(out, _zf, order=spline_order, mode="nearest") + new_im = out + else: + new_im = ndi.zoom(data, zoom_np, order=spline_order) if conserve_flux: new_im = np.nan_to_num(new_im, copy=False) sum_new = np.sum(new_im) if sum_new != 0: new_im *= sum_orig / sum_new + elif sum_orig != 0: + logger.warning( + "rescale_imagehdu: all input flux (%g) was lost in resampling; " + "flux cannot be conserved.", sum_orig) imagehdu.data = new_im @@ -572,8 +629,16 @@ def rescale_imagehdu(imagehdu: fits.ImageHDU, pixel_scale: float | u.Quantity, # # The transformation only applies to spatial coordinates, which we # assume to be the first two in the WCS. + # CDELT keeps the nominal zoom (CDELT' == pixel_scale exactly). CRPIX + # uses the realised zoom n_out/n_in (ndi.zoom / the rebin both produce + # round(n_in*zoom) pixels), else the reference pixel -- and every source + # -- is misplaced by up to half an output pixel when n_in*zoom is not + # integer. + n_in_sp = in_shape[::-1][:2].astype(float) + n_out_sp = np.array(imagehdu.data.shape[::-1][:2], dtype=float) + zoom_real = n_out_sp / n_in_sp ww.wcs.cdelt[:2] /= zoom[:2] - ww.wcs.crpix[:2] = 0.5 + (ww.wcs.crpix[:2] - 0.5) * zoom[:2] + ww.wcs.crpix[:2] = 0.5 + (ww.wcs.crpix[:2] - 0.5) * zoom_real logger.debug("new crpix %s", ww.wcs.crpix) imagehdu.header.update(ww.to_header()) @@ -955,9 +1020,14 @@ def calc_footprint(header, wcs_suffix="", new_unit: str = None): return xy1 -def calc_table_footprint(table: Table, x_name: str, y_name: str, - tbl_unit: str, new_unit: str, - padding=None) -> np.ndarray: +def calc_table_footprint( + table: Table, + x_name: str, + y_name: str, + tbl_unit: str, + new_unit: str, + padding: u.Quantity | None = None, +) -> NDArray: """ Equivalent to ``calc_footprint()``, but for tables instead of images. @@ -1068,10 +1138,12 @@ def _get_unit_from_headers(*headers, wcs_suffix: str = "") -> str: return unit -def det_wcs_from_sky_wcs(sky_wcs: WCS, - pixel_scale: float, - plate_scale: float, - naxis=None) -> tuple[WCS, np.ndarray]: +def det_wcs_from_sky_wcs( + sky_wcs: WCS, + pixel_scale: float, + plate_scale: float, + naxis: tuple[int, int] | None = None, +) -> tuple[WCS, NDArray]: """ Create detector WCS from celestial WCS using pixel and plate scales. diff --git a/scopesim/optics/optical_train.py b/scopesim/optics/optical_train.py index 4b3448cb2..00823c7d8 100644 --- a/scopesim/optics/optical_train.py +++ b/scopesim/optics/optical_train.py @@ -289,6 +289,10 @@ def observe(self, orig_source=None, update=True, **kwargs): desc=" FOV effects", position=1):#, leave=False): fov = effect.apply_to(fov) + if fov.hdu is None: + logger.info(" Skipping empty FOV") + continue + if self.cmds.get("!INST.flatten", True): fov.flatten() self.image_planes[fov.image_plane_id].add(fov.hdu, wcs_suffix="D") @@ -505,93 +509,29 @@ def readout(self, filename=None, reset=True, **kwargs): return copy.deepcopy(hduls) def write_header(self, hdulist): - """Write meaningful header to simulation product.""" - # Primary hdu + """Write minimal header to output HDUL. + + This method exists as a fallback of last resort, if no FITS header + effect is present in the optical train. It should not be expanded, + instead any other keywords required in the output should be included + via those effects. This should only be called for very minimal + instrument setups or during testing and debugging, where most effects + are switched off. + """ + # Primary HDU pheader = hdulist[0].header pheader["DATE"] = datetime.now().isoformat(timespec="seconds") pheader["ORIGIN"] = f"Scopesim {__version__}" + + # These are always present in cmds, so should be save pheader["INSTRUME"] = from_currsys("!OBS.instrument", self.cmds) pheader["INSTMODE"] = ", ".join(from_currsys("!OBS.modes", self.cmds)) - pheader["TELESCOP"] = from_currsys("!TEL.telescope", self.cmds) - pheader["LOCATION"] = from_currsys("!ATMO.location", self.cmds) - - # Source information taken from first only. - # ..todo: What if source is a composite? - srcfield = self._last_source.fields[0] - if type(srcfield).__name__ == "Table": - pheader["SOURCE"] = "Table" - elif type(srcfield).__name__ == "ImageHDU": - if "BG_SURF" in srcfield.header: - pheader["SOURCE"] = srcfield.header["BG_SURF"] - else: - try: - pheader["SOURCE"] = srcfield.header["FILENAME"] - except KeyError: - pheader["SOURCE"] = "ImageHDU" - - # Image hdul - # ..todo: currently only one, update for detector arrays - # ..todo: normalise filenames - some need from_currsys, some need Path(...).name - # this should go into a function so as to reduce clutter here. - iheader = hdulist[1].header - iheader["EXPTIME"] = from_currsys("!OBS.exptime", self.cmds), "[s]" - iheader["DIT"] = from_currsys("!OBS.dit", self.cmds), "[s]" - iheader["NDIT"] = from_currsys("!OBS.ndit", self.cmds) - iheader["BUNIT"] = "e", "per EXPTIME" - iheader["PIXSCALE"] = from_currsys("!INST.pixel_scale", self.cmds), "[arcsec]" - - for eff in self.optics_manager.detector_setup_effects: - efftype = type(eff).__name__ - - if efftype == "DetectorList" and eff.include: - iheader["DETECTOR"] = eff.meta["detector"] - - for eff in self.optics_manager.detector_array_effects: - efftype = type(eff).__name__ - - if (efftype == "DetectorModePropertiesSetter" and - eff.include): - # ..todo: can we write this into currsys? - iheader["DET_MODE"] = (eff.meta["detector_readout_mode"], - "detector readout mode") - iheader["MINDIT"] = from_currsys("!DET.mindit", self.cmds), "[s]" - iheader["FULLWELL"] = from_currsys("!DET.full_well", self.cmds), "[s]" - iheader["RON"] = from_currsys("!DET.readout_noise", self.cmds), "[e]" - iheader["DARK"] = from_currsys("!DET.dark_current", self.cmds), "[e/s]" - - ifilter = 1 # Counts filter wheels - isurface = 1 # Counts surface lists - for eff in self.optics_manager.source_effects: - efftype = type(eff).__name__ - - if efftype == "ADCWheel" and eff.include: - iheader["ADC"] = eff.current_adc.meta["name"] - - if efftype == "FilterWheel" and eff.include: - iheader[f"FILTER{ifilter}"] = (eff.current_filter.meta["name"], - eff.meta["name"]) - ifilter += 1 - - if efftype == "SlitWheel" and eff.include: - iheader["SLIT"] = (eff.current_slit.meta["name"], - eff.meta["name"]) - - if efftype == "PupilTransmission" and eff.include: - iheader["PUPTRANS"] = (from_currsys("!OBS.pupil_transmission", self.cmds), - "cold stop, pupil transmission") - - if efftype == "SkycalcTERCurve" and eff.include: - iheader["ATMOSPHE"] = "Skycalc", "atmosphere model" - iheader["LOCATION"] = eff.meta["location"] - iheader["AIRMASS"] = eff.meta["airmass"] - iheader["TEMPERAT"] = eff.meta["temperature"], "[degC]" - iheader["HUMIDITY"] = eff.meta["humidity"] - iheader["PRESSURE"] = eff.meta["pressure"], "[hPa]" - iheader["PWV"] = eff.meta["pwv"], "precipitable water vapour" - - if efftype == "SurfaceList" and eff.include: - iheader[f"SURFACE{isurface}"] = eff.meta["name"] - isurface += 1 + + # Image HDUs + for hdu in hdulist[1:]: + hdu.header["EXPTIME"] = from_currsys("!OBS.exptime", self.cmds), "[s]" + hdu.header["DIT"] = from_currsys("!OBS.dit", self.cmds), "[s]" + hdu.header["NDIT"] = from_currsys("!OBS.ndit", self.cmds) return hdulist diff --git a/scopesim/reports/rst_utils.py b/scopesim/reports/rst_utils.py index b6e854b1c..c5e830ed2 100644 --- a/scopesim/reports/rst_utils.py +++ b/scopesim/reports/rst_utils.py @@ -314,7 +314,7 @@ def latexify_rst_text(rst_text, filename=None, path=None, title_char="=", text = "Title\n<<<<<\nSubtitle\n>>>>>>>>\n\n" parts = publish_parts( text + rst_text, - writer_name="latex", + writer="latex", # Settings_overrides to placate FutureWarnings. # TODO: Decide whether the future defaults look better. settings_overrides={ diff --git a/scopesim/server/atmo_registry.txt b/scopesim/server/atmo_registry.txt index 4fb92d451..77ae643d1 100644 --- a/scopesim/server/atmo_registry.txt +++ b/scopesim/server/atmo_registry.txt @@ -1 +1,2 @@ Leiden_atmo_ter.fits 16b5faa1bc4e75ba2d34cad02f302223b8a788306cd34b9809c3e895b46132b0 +MICADO_relay_air_trans.fits bea89caf7b6690790c409f7ed518b7e0783ce98098ac1b7343025b2d9173b04b diff --git a/scopesim/server/database.py b/scopesim/server/database.py index f7d92560d..0800b50b1 100644 --- a/scopesim/server/database.py +++ b/scopesim/server/database.py @@ -10,7 +10,6 @@ import httpxyz as httpx from more_itertools import first, last, groupby_transform -from .github_utils import download_github_folder from .download_utils import ( get_server_folder_contents, handle_download, @@ -33,40 +32,25 @@ class PkgNotFoundError(Exception): """Unable to find given package or given release of that package.""" -def get_server_package_list(): - """Deprecated since v0.6.0.""" - raise AttributeError( - "The singular variant of this function has been deprecated since " - "version 0.6.0 of ScopeSim and will be completely removed in v0.12.") - - def _get_package_name(package: str) -> str: return package.split(".", maxsplit=1)[0] -def _parse_raw_version(raw_version: str) -> str: - """Catch initial package version which has no date info. - - Set initial package version to basically "minus infinity". - """ - if raw_version in ("", "zip"): - return str(date(1, 1, 1)) - return raw_version.strip(".zip") - - -def _unparse_raw_version(raw_version: str, package_name: str) -> str: - """Turn version string back into full zip folder name. - - If initial version was set with `_parse_raw_version`, revert that. - """ - if raw_version == str(date(1, 1, 1)): - return f"{package_name}.zip" - return f"{package_name}.{raw_version}.zip" - - def _parse_package_version(package: str) -> tuple[str, str]: + """Split zip folder name into package name and version.""" p_name, p_version = package.split(".", maxsplit=1) - return p_name, _parse_raw_version(p_version) + return p_name, p_version.strip(".zip") + + +def _unparse_package_version( + package_name: str, + version: str, + suffix: str | None = None, +) -> str: + """Turn version string back into full zip folder name.""" + if suffix is None: + return f"{package_name}.{version}.zip" + return f"{package_name}.{version}.{suffix}.zip" def _is_stable(package_version: str) -> bool: @@ -123,10 +107,9 @@ def get_all_latest(version_groups: _GrpVerType) -> Iterator[tuple[str, str]]: def group_package_versions(all_packages: Iterable[tuple[str, str]]) -> _GrpItrType: """Group different versions of packages by package name.""" - version_groups = groupby_transform(sorted(all_packages), - keyfunc=first, - valuefunc=last, - reducefunc=list) + version_groups = groupby_transform( + sorted(all_packages), keyfunc=first, valuefunc=last, reducefunc=list, + ) return version_groups @@ -269,7 +252,7 @@ def list_packages(pkg_name: str | None = None) -> list[str]: if pkg_name not in all_grouped: raise ValueError(f"Package name {pkg_name} not found on server.") - p_versions = [_unparse_raw_version(version, pkg_name) + p_versions = [_unparse_package_version(pkg_name, version) for version in all_grouped[pkg_name]] return p_versions @@ -280,14 +263,14 @@ def _get_zipname(pkg_name: str, release: str, all_versions) -> str: elif release == "latest": zip_name = get_latest(all_versions[pkg_name]) else: - release = _parse_raw_version(release) + release = release.strip(".zip") if release not in all_versions[pkg_name]: msg = (f"Requested version '{release}' of '{pkg_name}' package" " could not be found on the server. Available versions " f"are: {all_versions[pkg_name]}") raise ValueError(msg) zip_name = release - return _unparse_raw_version(zip_name, pkg_name) + return _unparse_package_version(pkg_name, zip_name) def _download_single_package( @@ -315,17 +298,11 @@ def _download_single_package( save_dir.mkdir(parents=True, exist_ok=True) if "github" in release: - warn("Downloading IRDB packages directly from GitHub is deprecated " - "and will raise an error from ScopeSim version 0.12 onwards. " - "If you really need to use an unreleased version of an IRDB " - "package, please use a local clone of the IRDB repo instead.", - FutureWarning, stacklevel=3) - - base_url = "https://github.com/AstarVienna/irdb/tree/" - github_hash = release.split(":")[-1].split("@")[-1] - pkg_url = f"{base_url}{github_hash}/{pkg_name}" - download_github_folder(repo_url=pkg_url, output_dir=save_dir) - return save_dir.absolute() + raise ValueError( + "Downloading IRDB packages directly from GitHub is deprecated. " + "If you really need to use an unreleased version of an IRDB " + "package, please use a local clone of the IRDB repo instead." + ) zip_name = _get_zipname(pkg_name, release, all_versions) pkg_url = f"{folders_dict[pkg_name]}/{zip_name}" @@ -433,7 +410,11 @@ def download_packages( def download_missing_pkgs(instrument: str) -> None: """Download instrument package and required support packages.""" # First download package itself - zip_file = download_packages([instrument])[0] + downloaded = download_packages([instrument]) + if not downloaded: + raise RuntimeError(f"Could not download \"{instrument}\".") + + zip_file = downloaded[0] # If package needs other packages, download them as well defyam = zip_file.with_suffix("") / "default.yaml" @@ -460,15 +441,3 @@ def check_packages(instrument: str, download_missing: bool) -> None: raise ValueError( f"IRDB package for {instrument} not found, auto-download is " "disabled. Please set package directory or download package.") - - -# ============================================================================== -# Funtions below from from OLD_database.py -# ============================================================================== - -def download_package(*args, **kwargs): - """Deprecated since v0.5.0.""" - raise AttributeError( - "The singular variant of this function has been deprecated since " - "version 0.5.0 of ScopeSim. It will be completely removed in version " - "0.12. Please use ``download_packages`` (plural variant) instead!") diff --git a/scopesim/server/example_data_utils.py b/scopesim/server/example_data_utils.py index ef82bc9d6..3ea1d65b3 100644 --- a/scopesim/server/example_data_utils.py +++ b/scopesim/server/example_data_utils.py @@ -88,13 +88,6 @@ def download_example_data( will be silently passed to the `retriever`. """ - if isinstance(files[0], list): - raise TypeError( - "Passing a list to download_example_data is deprecated. " - "Simply pass filenames as *args, i.e. " - "download_example_data(\"foo.fits\", \"bar.fits\")." - ) - retriever = create_retriever("example_data", save_dir, url) save_paths = [] diff --git a/scopesim/server/github_utils.py b/scopesim/server/github_utils.py deleted file mode 100644 index dbae348c8..000000000 --- a/scopesim/server/github_utils.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Used only by the `database` submodule. - -Original comment for these functions: - 2022-04-10 (KL) - Code taken directly from https://github.com/sdushantha/gitdir - Adapted for ScopeSim usage. - Many thanks to the authors! - -""" - -import re -from warnings import warn -from pathlib import Path - -from .download_utils import handle_download, send_get, create_client -from ..utils import get_logger - - -logger = get_logger(__name__) - - -def create_github_url(url: str) -> None: - """ - From the given url, produce a URL compatible with Github's REST API. - - Can handle blob or tree paths. - - .. deprecated:: 0.11.0 - - This function is deprecated and will be removed in version 0.12. - """ - warn("The function ``create_github_url`` is deprecated and will be " - "removed in version 0.12.", DeprecationWarning, stacklevel=2) - - repo_only_url = re.compile(r"https:\/\/github\.com\/[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}\/[a-zA-Z0-9]+$") - re_branch = re.compile("/(tree|blob)/(.+?)/") - - # Check if the given url is a url to a GitHub repo. If it is, tell the - # user to use 'git clone' to download it - if re.match(repo_only_url, url): - message = ("✘ The given url is a complete repository. Use 'git clone'" - " to download the repository") - logger.error(message) - raise ValueError(message) - - # extract the branch name from the given url (e.g master) - branch = re_branch.search(url) - download_dirs = url[branch.end():] - api_url = (url[:branch.start()].replace("github.com", "api.github.com/repos", 1) + - f"/contents/{download_dirs}?ref={branch.group(2)}") - return api_url, download_dirs - - -def download_github_folder(repo_url: str, - output_dir: Path | str = "./") -> None: - """ - Download the files and directories in repo_url. - - Re-written based on the on the download function - `here `_ - - .. deprecated:: 0.11.0 - - This function is deprecated and will be removed in version 0.12. - """ - warn("The function ``download_github_folder`` is deprecated and will be " - "removed in version 0.12.", DeprecationWarning, stacklevel=2) - - output_dir = Path(output_dir) - - # convert repo_url into an api_url - api_url, download_dirs = create_github_url(repo_url) - - # get the contents of the github folder - with create_client("", cached=False) as client: - data = send_get(client, api_url).json() - - # Make the base directories for this GitHub folder - (output_dir / download_dirs).mkdir(parents=True, exist_ok=True) - - for entry in data: - # if the entry is a further folder, walk through it - if entry["type"] == "dir": - download_github_folder(repo_url=entry["html_url"], - output_dir=output_dir) - - # if the entry is a file, download it - elif entry["type"] == "file": - # download the file - save_path = output_dir / entry["path"] - handle_download(client, entry["download_url"], save_path, - entry["path"], padlen=0, disable_bar=True) - logger.info("Downloaded: %s", entry["path"]) diff --git a/scopesim/source/source_fields.py b/scopesim/source/source_fields.py index 3ca2a2c18..ecd315cf7 100644 --- a/scopesim/source/source_fields.py +++ b/scopesim/source/source_fields.py @@ -62,6 +62,7 @@ class HDUSourceField{+wcs} # rename it dataclass_field to avoid confusion with source field import numpy as np +from numpy.typing import ArrayLike, NDArray from astropy.table import Table, Column from astropy.io.registry import IORegistryError @@ -127,7 +128,7 @@ def name(self) -> str: """Name of the object (if set).""" return self.meta.get("object", "") - def get_corners(self, unit: u.Unit | str = "arcsec") -> np.ndarray: + def get_corners(self, unit: u.Unit | str = "arcsec") -> NDArray: """Calculate and return footprint corner points in `unit`. .. versionadded:: 0.10.0 @@ -187,9 +188,15 @@ def from_file(cls, filename: Path | str, return cls(tbl, spectra=spectra, meta=kwargs) @classmethod - def from_arrays(cls, x, y, ref, weight, - spectra: dict[int, SourceSpectrum], - **kwargs): + def from_arrays( + cls, + x: ArrayLike, + y: ArrayLike, + ref: ArrayLike, + weight: ArrayLike, + spectra: dict[int, SourceSpectrum], + **kwargs, + ): """Construct source table from arrays for each column.""" if weight is None: weight = np.ones(len(x)) @@ -222,7 +229,7 @@ def _write_stream(self, stream: TextIO) -> None: stream.write(f"Table with {len(self)} rows, referencing " f"spectra {set(self.spectra)}") - def get_corners(self, unit: u.Unit | str = "arcsec") -> np.ndarray: + def get_corners(self, unit: u.Unit | str = "arcsec") -> NDArray: """Calculate and return footprint corner points in `unit`.""" x_qty = self.field["x"].quantity.to(unit).value y_qty = self.field["y"].quantity.to(unit).value @@ -283,7 +290,7 @@ def header(self) -> fits.Header: return self.field.header @property - def data(self) -> np.ndarray: + def data(self) -> NDArray: """Shortcut for `field.data`.""" return self.field.data @@ -333,7 +340,7 @@ def _write_stream(self, stream: TextIO) -> None: stream.write(f"ImageHDU with size {self.img_size}, referencing " f"spectrum {self.field.header.get('SPEC_REF', '-')}") - def get_corners(self, unit: u.Unit | str = "arcsec") -> np.ndarray: + def get_corners(self, unit: u.Unit | str = "arcsec") -> NDArray: """Calculate and return footprint corner points in `unit`.""" return imp_utils.calc_footprint(self.header, new_unit=unit) @@ -445,7 +452,7 @@ class BackgroundSourceField(SpectrumSourceField): header: fits.Header - def get_corners(self, unit: u.Unit | str = "arcsec") -> np.ndarray: + def get_corners(self, unit: u.Unit | str = "arcsec") -> NDArray: """Return imaginary corner from + to - infinity.""" return np.array([-np.inf, np.inf]) diff --git a/scopesim/tests/mocks/MOSAIC/TRACE_MOS-LR-J.fits b/scopesim/tests/mocks/MOSAIC/TRACE_MOS-LR-J.fits new file mode 100644 index 000000000..ef98b21bd Binary files /dev/null and b/scopesim/tests/mocks/MOSAIC/TRACE_MOS-LR-J.fits differ diff --git a/scopesim/tests/tests_effects/test_AtmoLibraryTERCurve.py b/scopesim/tests/tests_effects/test_AtmoLibraryTERCurve.py index 0ffbbea15..3d2f9a178 100644 --- a/scopesim/tests/tests_effects/test_AtmoLibraryTERCurve.py +++ b/scopesim/tests/tests_effects/test_AtmoLibraryTERCurve.py @@ -15,9 +15,17 @@ class TestLocalFile: def test_initialises_from_local_file(self, mock_path): with patch("scopesim.rc.__search_path__", [mock_path]): atmo = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="pwv", pwv=1) assert isinstance(atmo, AtmoLibraryTERCurve) + def test_fails_with_wrong_parameter(self, mock_path): + with patch("scopesim.rc.__search_path__", [mock_path]): + with pytest.raises(KeyError): + _ = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="relH", + relH=50) + @pytest.mark.parametrize("pwv, extname", [(1.0, "PWV_01"), (23, "PWV_23"), @@ -26,6 +34,7 @@ def test_initialises_from_local_file(self, mock_path): def test_picks_nearest_pwv(self, pwv, extname, mock_path): with patch("scopesim.rc.__search_path__", [mock_path]): atmo = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="pwv", pwv=pwv) assert atmo.meta['extname'] == extname assert atmo.meta['pwv'] == pwv @@ -34,6 +43,7 @@ def test_updates_with_new_pwv(self, mock_path): with patch("scopesim.rc.__search_path__", [mock_path]): oldpwv, newpwv = 1, 22.5 atmo = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="pwv", pwv=oldpwv) assert atmo.meta['extname'] == "PWV_01" assert atmo.meta['pwv'] == oldpwv @@ -45,6 +55,7 @@ def test_update_with_unknown_parameter_issues_warning(self, mock_path, caplog): caplog.set_level(logging.WARNING) with patch("scopesim.rc.__search_path__", [mock_path]): atmo = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="pwv", pwv=1.) atmo.update(temp=23) assert "Can only update with parameter pwv" in caplog.text diff --git a/scopesim/tests/tests_effects/test_ConstPSF.py b/scopesim/tests/tests_effects/test_ConstPSF.py index 7f6ae9a81..d6270dd1c 100644 --- a/scopesim/tests/tests_effects/test_ConstPSF.py +++ b/scopesim/tests/tests_effects/test_ConstPSF.py @@ -17,7 +17,6 @@ """ import pytest -from pytest import approx import numpy as np from numpy import testing as npt @@ -35,15 +34,17 @@ # pylint: disable=missing-class-docstring, # pylint: disable=missing-function-docstring -@pytest.fixture(scope="function") -def centre_fov(): +@pytest.fixture(name="centre_fov", scope="function") +def fixture_centre_fov(): return _centre_fov() -@pytest.fixture(scope="function") -def const_psf(mock_path): +@pytest.fixture(name="const_psf", scope="function") +def fixture_const_psf(mock_path): + """Instantiate a field constant psf with linear interpolation""" constpsf = FieldConstantPSF( - filename=str(mock_path / "test_ConstPSF.fits")) + filename=str(mock_path / "test_ConstPSF.fits"), + interp_order=1) return constpsf @@ -98,9 +99,9 @@ def test_returns_array_with_single_kernel_from_fov( @pytest.mark.parametrize("scale_factor", ( pytest.param(1/3, marks=pytest.mark.xfail(reason="PSF center off")), 1, - pytest.param(5, marks=pytest.mark.xfail(reason="1x1 array turned into scalar")), + 5 )) - def test_kernel_is_scale_properly_if_cdelts_differ( + def test_kernel_is_scaled_properly_if_cdelts_differ( self, scale_factor, const_psf, diff --git a/scopesim/tests/tests_effects/test_MetisLMSTraceList.py b/scopesim/tests/tests_effects/test_MetisLMSTraceList.py index 7fbfd277b..3cf1dc096 100644 --- a/scopesim/tests/tests_effects/test_MetisLMSTraceList.py +++ b/scopesim/tests/tests_effects/test_MetisLMSTraceList.py @@ -16,6 +16,30 @@ def patch_mock_path_metis(mock_dir): with patch("scopesim.rc.__search_path__", [metis_dir]): yield +class TestDetectorLayoutCache: + def test_layout_file_is_read_only_once(self, mock_dir, monkeypatch): + from scopesim.effects import metis_lms_trace_list as mlt + + mlt._read_detector_layout.cache_clear() + calls = [] + real_read = mlt.ioascii.read + + def counting_read(*args, **kwargs): + calls.append(args) + return real_read(*args, **kwargs) + + monkeypatch.setattr(mlt.ioascii, "read", counting_read) + + path = str(mock_dir / "files" / "LIST_detector_layout.dat") + first = mlt._read_detector_layout(path) + second = mlt._read_detector_layout(path) + + assert len(calls) == 1 + assert first is second + mlt._read_detector_layout.cache_clear() + + + @pytest.mark.usefixtures("patch_mock_path_metis") class TestPredisperserAngle: @pytest.mark.parametrize("coeffs,wavelen,expected", diff --git a/scopesim/tests/tests_effects/test_MetisWCU.py b/scopesim/tests/tests_effects/test_MetisWCU.py index 5261e4065..d97c92726 100644 --- a/scopesim/tests/tests_effects/test_MetisWCU.py +++ b/scopesim/tests/tests_effects/test_MetisWCU.py @@ -40,6 +40,15 @@ def fixture_bbsource(): bb_temp=1000*u.K, is_temp=300*u.K, wcu_temp=300*u.K, + laser_l_wave=3.39, # [um] + laser_l_power=5e-3, # [W] + laser_t_wave=[4.70], # [um], array + laser_t_wave_limits=[1., 30.], # [um] + laser_t_power=70e-3, # [W] + laser_m_wave=5.26, # [um] + laser_m_power=20e-3, # [W] + laser_n_wave=10.4, # [um] + laser_n_power=2.e-3, # [W] bb_aperture=1., bb_to_is=None, rho_tube=0.95, @@ -211,7 +220,18 @@ def test_laser_brighter_with_fibre_transmission(self, bbsource): bbsource.set_lamp("laser") intens_2 = np.sum(bbsource.intens_lamp) - assert intens_2 / intens_1 == ft_2 / ft_1 + npt.assert_allclose(intens_2 / intens_1, ft_2 / ft_1) + + + def test_can_tune_laser(self, bbsource): + bbsource.set_lamp("laser") + assert bbsource.meta["laser_t_wave"] == [4.70] + bbsource.tune_laser(4.72) + assert bbsource.meta["laser_t_wave"] == [4.72] + bbsource.tune_laser(np.linspace(4.68, 4.72, 10)) + assert len(bbsource.meta["laser_t_wave"]) == 10 + bbsource.tune_laser([]) + assert bbsource.meta["laser_t_wave"] == [0.001] @pytest.fixture(name="fpmask", scope="function") diff --git a/scopesim/tests/tests_effects/test_SpectralTraceList.py b/scopesim/tests/tests_effects/test_SpectralTraceList.py index fbc460ab3..ca634a08e 100644 --- a/scopesim/tests/tests_effects/test_SpectralTraceList.py +++ b/scopesim/tests/tests_effects/test_SpectralTraceList.py @@ -72,11 +72,7 @@ def fixture_spectral_trace_list(): """Instantiate a SpectralTraceList""" return SpectralTraceList(hdulist=tlo.make_trace_hdulist()) -class TestRectification: - def test_rectify_cube_not_implemented(self, spectral_trace_list): - hdulist = fits.HDUList() - with pytest.raises(NotImplementedError): - spectral_trace_list.rectify_cube(hdulist) +#class TestRectification: # def test_rectify_traces_needs_ximin_and_ximax(self, spectral_trace_list): # hdulist = fits.HDUList([fits.PrimaryHDU()]) diff --git a/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py b/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py index 21dadc9ae..ca2f19d2b 100644 --- a/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py +++ b/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py @@ -9,9 +9,12 @@ from astropy.io import fits +from astropy import units as u + from scopesim.effects.spectral_trace_list_utils import SpectralTrace from scopesim.effects.spectral_trace_list_utils import Transform2D, power_vector from scopesim.effects.spectral_trace_list_utils import make_image_interpolations +from scopesim.effects.spectral_trace_list_utils import XiLamImage from scopesim.tests.mocks.py_objects import trace_list_objects as tlo class TestSpectralTrace: @@ -36,6 +39,17 @@ def test_determines_correct_dispersion_axis_y(self): spt = SpectralTrace(trace_tbl) assert spt.dispersion_axis == 'y' +class TestSpectralTracePlot: + """Tests for SpectralTrace.plot()""" + @pytest.mark.parametrize("plot_outline", [True, False]) + def test_plot_trace_id_works_without_outline(self, plot_outline): + # plot_trace_id previously relied on corners, which was only + # computed when plot_outline was True + spt = SpectralTrace(tlo.trace_1(), trace_id="TRACE_1") + axes = spt.plot(plot_trace_id=True, plot_outline=plot_outline) + assert any(txt.get_text() == spt.trace_id for txt in axes.texts) + + class TestPowerVec: """Test function power_vector()""" def test_gives_correct_result(self): @@ -128,12 +142,47 @@ def test_fit_gives_correct_matrix(self): assert tf2d.matrix == pytest.approx(matrix) + def test_call_kwargs_do_not_modify_instance(self, quadratic): + # pre/posttransform overrides passed to __call__ previously + # rewired the instance for all subsequent calls + tf2d = Transform2D(quadratic['matrix']) + x, y = 0.7, -0.3 + plain = tf2d(x, y) + shifted = tf2d(x, y, posttransform=lambda z: z + 100) + assert shifted == pytest.approx(plain + 100) + assert tf2d.posttransform is None + assert tf2d(x, y) == pytest.approx(plain) + def test_grid_false_shape_is_preserved(self, tf2d): n_x, n_y = 4, 2 res = tf2d(np.ones((n_y, n_x)), np.ones((n_y, n_x)), grid=False) assert res.shape == (n_y, n_x) +class MockCubeFov: + """Minimal stand-in for a FieldOfView carrying a spectral cube.""" + def __init__(self, n_lam=20, n_eta=3, n_xi=11): + hdr = fits.Header() + hdr["NAXIS"] = 3 + hdr["NAXIS1"], hdr["NAXIS2"], hdr["NAXIS3"] = n_xi, n_eta, n_lam + hdr["CTYPE1"], hdr["CTYPE2"], hdr["CTYPE3"] = "LINEAR", "LINEAR", "WAVE" + hdr["CRPIX1"], hdr["CRPIX2"], hdr["CRPIX3"] = 1, 1, 1 + hdr["CRVAL1"], hdr["CRVAL2"], hdr["CRVAL3"] = 0., 0., 2.0 + hdr["CDELT1"], hdr["CDELT2"], hdr["CDELT3"] = 0.1, 0.1, 0.001 + hdr["CUNIT1"], hdr["CUNIT2"], hdr["CUNIT3"] = "arcsec", "arcsec", "um" + self.cube = fits.ImageHDU( + data=np.ones((n_lam, n_eta, n_xi)), header=hdr) + self.meta = {"xi_min": -0.5 * u.arcsec, "xi_max": 0.5 * u.arcsec} + + +class TestXiLamImage: + def test_primary_wcs_keeps_arcsec_cunit(self): + # The wcsa block previously overwrote self.wcs.wcs.cunit + xilam = XiLamImage(MockCubeFov(), dlam_per_pix=0.001) + assert list(xilam.wcs.wcs.cunit) == [u.um, u.arcsec] + assert list(xilam.wcsa.wcs.cunit) == [u.um, u.dimensionless_unscaled] + + class TestImageInterpolations: """Tests for function make_image_interpolations""" def test_return_empty_for_table(self): diff --git a/scopesim/tests/tests_effects/test_mosaic_trace_list.py b/scopesim/tests/tests_effects/test_mosaic_trace_list.py index 96187e2d6..da3207e76 100644 --- a/scopesim/tests/tests_effects/test_mosaic_trace_list.py +++ b/scopesim/tests/tests_effects/test_mosaic_trace_list.py @@ -3,14 +3,20 @@ # pylint: disable=missing-function-docstring # pylint: disable=invalid-name # pylint: disable=too-few-public-methods +from unittest.mock import patch import pytest import numpy as np - from astropy.io import fits +from scopesim.effects.mosaic_trace_list import (Transform1D, + MosaicSpectralTraceList, + MosaicSpectralTrace, + MosaicOutputFormat) +from scopesim.optics.image_plane_utils import header_from_list_of_xy +from scopesim.detector import Detector -from scopesim.utils import power_vector -from scopesim.effects.mosaic_trace_list import Transform1D +import logging +LOGGER = logging.getLogger(__name__) @pytest.fixture(name="tf1d", scope="class") def fixture_tf1d(): @@ -62,3 +68,136 @@ def test_fit_gives_correct_coeffs(self): tf1d = Transform1D.fit(x, y, degree=3) assert tf1d.coeffs == pytest.approx(coeffs) + + +### Fixtures to test the classes +@pytest.fixture(scope="class") +def patch_mock_path_mosaic(mock_dir): + mosaic_dir = mock_dir / "MOSAIC" + with patch("scopesim.rc.__search_path__", [mosaic_dir]): + yield + +@pytest.fixture(name="tracelist", scope="class") +def fixture_tracelist(): + """Instantiate a MosaicSpectralTraceList""" + return MosaicSpectralTraceList(filename="TRACE_MOS-LR-J.fits") + +@pytest.fixture(name="collapse1d", scope="class") +def fixture_collapse1d(): + """Instantiate a CollapseSpectralTrace""" + return MosaicOutputFormat(filename="TRACE_MOS-LR-J.fits", format="collapse1d") + +@pytest.fixture(name="convert2table", scope="class") +def fixture_convert2table(): + """Instantiate a ConvertToTable""" + return MosaicOutputFormat(filename="TRACE_MOS-LR-J.fits", format="table") + +@pytest.fixture(name="image", scope="class") +def fixture_image(): + """Instantiate OutputFormat with "image". """ + return MosaicOutputFormat(filename="TRACE_MOS-LR-J.fits", format="image") + +@pytest.fixture(name="detector", scope="class") +def fixture_detector(): + """Instantiate a detector image""" + rows = np.array([2023, 2031, 2039, 2047, 2055, 2063, 2071]) + xsize, ysize = 50, 4096 + hdr = header_from_list_of_xy([-xsize/2, xsize/2], [-ysize/2, ysize/2], 1, "D") + dtcr = Detector(hdr) + dtcr._hdu.data[rows, :] = 1 + return dtcr + +@pytest.fixture(name="apply_collapse1d", scope="class") +def fixture_apply_collapse1d(collapse1d, detector): + return collapse1d.apply_to(detector) + +@pytest.fixture(name="apply_converttotable", scope="class") +def fixture_apply_converttotable(convert2table, detector): + return convert2table.apply_to(detector) + +@pytest.fixture(name="apply_image", scope="class") +def fixture_apply_image(image, detector): + return image.apply_to(detector) + + +@pytest.mark.usefixtures("patch_mock_path_mosaic") +class TestSpectralTraceList: + """Tests for MosaicSpectralTraceList""" + def test_initialise_tracelist(self, tracelist): + assert isinstance(tracelist, MosaicSpectralTraceList) + + def test_tracelist_loads_aperture_list(self, tracelist): + assert len(tracelist.aplist) == 7 + assert "right" in tracelist.aplist.colnames + + def test_tracelist_creates_traces(self, tracelist): + assert len(tracelist.spectral_traces) == 7 + + def test_trace_has_transforms(self, tracelist): + for trace in tracelist.spectral_traces.values(): + assert isinstance(trace.lam2x, Transform1D) + assert isinstance(trace.lam2y, Transform1D) + assert isinstance(trace.x2lam, Transform1D) + + +@pytest.mark.usefixtures("patch_mock_path_mosaic") +class TestCollapse1D: + """Tests for MOSAIC MOS collapse to 1D spectrum""" + def test_initialise_collapse1d(self, collapse1d): + assert isinstance(collapse1d, MosaicOutputFormat) + + def test_collapse1d_returns_nondetector(self, collapse1d, caplog): + caplog.set_level(logging.WARNING) + obj = ["Not", "a", "detector"] + assert collapse1d.apply_to(obj) == obj + assert "MosaicOutputFormat" in caplog.text + + def test_collapse1d_returns_detector(self, apply_collapse1d): + assert isinstance(apply_collapse1d, Detector) + + def test_collapse1d_detector_has_bintable(self, apply_collapse1d): + assert isinstance(apply_collapse1d._hdu, fits.BinTableHDU) + + def test_collapse1d_gives_correct_result(self, apply_collapse1d): + assert np.all(apply_collapse1d._hdu.data['spectrum'] == 7) + + +@pytest.mark.usefixtures("patch_mock_path_mosaic") +class TestConvertToTable: + """Tests for MOSAIC mIFU table output""" + def test_initialise_table(self, convert2table): + assert isinstance(convert2table, MosaicOutputFormat) + + def test_converttotable_returns_nondetector(self, convert2table, caplog): + caplog.set_level(logging.WARNING) + obj = {"This": "is not", "a": "detector"} + assert convert2table.apply_to(obj) == obj + assert "MosaicOutputFormat" in caplog.text + + def test_converttotable_returns_detector(self, apply_converttotable): + assert isinstance(apply_converttotable, Detector) + + def test_converttotable_detector_has_bintable(self, apply_converttotable): + assert isinstance(apply_converttotable._hdu, fits.BinTableHDU) + + def test_converttotable_gives_correct_result(self, apply_converttotable): + for row in apply_converttotable._hdu.data: + assert np.all(row['spectrum'] == 1) + +@pytest.mark.usefixtures("patch_mock_path_mosaic") +class TestImage: + """Tests for MOSAIC MOS leaving detector image""" + def test_initialise_image(self, image): + assert isinstance(image, MosaicOutputFormat) + + def test_image_returns_nondetector(self, image, caplog): + caplog.set_level(logging.WARNING) + obj = ["Not", "a", "detector"] + assert image.apply_to(obj) == obj + assert "MosaicOutputFormat" in caplog.text + + def test_image_returns_detector(self, apply_image): + assert isinstance(apply_image, Detector) + + def test_image_detector_has_image(self, apply_image): + assert isinstance(apply_image._hdu, fits.ImageHDU) diff --git a/scopesim/tests/tests_optics/test_ImagePlane.py b/scopesim/tests/tests_optics/test_ImagePlane.py index 4bf42ccf2..1a8a81190 100644 --- a/scopesim/tests/tests_optics/test_ImagePlane.py +++ b/scopesim/tests/tests_optics/test_ImagePlane.py @@ -564,27 +564,47 @@ def test_mm_image_and_tables_on_large_canvas(self, input_table_mm, class TestImagePlaneAdd: - def test_add_many_tables_and_imagehdus(self, input_table, image_hdu_rect, - image_hdu_square): - tbl1 = deepcopy(input_table) - tbl2 = deepcopy(input_table) + def test_simple_add_imagehdu_conserves_flux(self, image_hdu_square, + image_hdu_rect): + fields = [image_hdu_rect, image_hdu_square] + hdr = imp_utils.get_canvas_header(pixel_scale=1 * u.arcsec, + hdu_or_table_list=fields) + + orig_sum = image_hdu_rect.data.sum() + + print(wcs.WCS(image_hdu_rect)) + print(wcs.WCS(hdr)) + + implane = opt_imp.ImagePlane(hdr) + implane.add(image_hdu_rect) + + if PLOTS: + plt.imshow(image_hdu_rect.data) + x, y = wcs.WCS(image_hdu_rect).wcs_world2pix(0, 0, 1) + print(x, y) + plt.plot(x, y, "ro") + plt.show() + + plt.imshow(implane.data) + x, y = wcs.WCS(image_hdu_rect).wcs_world2pix(0, 0, 1) + print(x, y) + plt.plot(x, y, "ro") + plt.show() - tbl1["y"] -= 50 - tbl2["x"] += 50 - tbl2["y"] += 50 + assert np.sum(implane.data) == approx(orig_sum, rel=1e-2) + def test_add_many_tables_and_imagehdus(self, image_hdu_rect, image_hdu_square): im_hdu = image_hdu_rect im_hdu.header["CRVAL1"] -= 150*u.arcsec.to(u.deg) im_hdu.header["CRVAL2"] += 20*u.arcsec.to(u.deg) - fields = [im_hdu, tbl1, tbl2, image_hdu_square] + fields = [im_hdu, image_hdu_square] hdr = imp_utils.get_canvas_header(fields, pixel_scale=1 * u.arcsec) implane = opt_imp.ImagePlane(hdr) implane.add(fields) - total_flux = np.sum(tbl1["flux"]) + np.sum(tbl2["flux"]) + \ - np.sum(im_hdu.data) + np.sum(image_hdu_square.data) + total_flux = np.sum(im_hdu.data) + np.sum(image_hdu_square.data) assert np.sum(implane.data) == approx(total_flux) if PLOTS: @@ -594,13 +614,6 @@ def test_add_many_tables_and_imagehdus(self, input_table, image_hdu_rect, x, y = imp_utils.val2pix(implane.header, x, y) plt.plot(x, y, "r-") - for tbl in [tbl1, tbl2]: - hdr = imp_utils._make_bounding_header_for_tables(tbl) - xy = imp_utils.calc_footprint(hdr) - x, y = xy[:, 0], xy[:, 1] - x, y = imp_utils.val2pix(implane.header, x, y) - plt.plot(x, y, "r-") - x0, y0 = imp_utils.val2pix(implane.header, 0, 0) plt.plot(x0, y0, "ro") plt.gca().set_aspect(1) @@ -608,29 +621,21 @@ def test_add_many_tables_and_imagehdus(self, input_table, image_hdu_rect, plt.imshow(implane.data, origin="lower", norm=LogNorm()) plt.show() - def test_add_many_mm_tables_and_imagehdus(self, input_table_mm, - image_hdu_rect_mm, - image_hdu_square_mm): + def test_add_many_mm_tables_and_imagehdus( + self, image_hdu_rect_mm, image_hdu_square_mm): image_hdu_rect = image_hdu_rect_mm image_hdu_square = image_hdu_square_mm - tbl1 = deepcopy(input_table_mm) - tbl2 = deepcopy(input_table_mm) - - tbl1["y_mm"] -= 50 - tbl2["x_mm"] += 50 - tbl2["y_mm"] += 50 im_hdu = image_hdu_rect im_hdu.header["CRVAL1D"] -= 150 # mm im_hdu.header["CRVAL2D"] += 20 - fields = [im_hdu, tbl1, tbl2, image_hdu_square] + fields = [im_hdu, image_hdu_square] hdr = imp_utils.get_canvas_header(fields, pixel_scale=1*u.mm) implane = opt_imp.ImagePlane(hdr) implane.add(fields, wcs_suffix="D") - total_flux = np.sum(tbl1["flux"]) + np.sum(tbl2["flux"]) + \ - np.sum(im_hdu.data) + np.sum(image_hdu_square.data) + total_flux = np.sum(im_hdu.data) + np.sum(image_hdu_square.data) assert np.sum(implane.data) == approx(total_flux) if PLOTS: @@ -640,14 +645,6 @@ def test_add_many_mm_tables_and_imagehdus(self, input_table_mm, x, y = imp_utils.val2pix(implane.header, x, y, "D") plt.plot(x, y, "r-") - for tbl in [tbl1, tbl2]: - hdr = imp_utils._make_bounding_header_for_tables(tbl, - pixel_scale=1*u.mm) - xy = imp_utils.calc_footprint(hdr, "D") - x, y = xy[:, 0], xy[:, 1] - x, y = imp_utils.val2pix(implane.header, x, y, "D") - plt.plot(x, y, "r-") - x0, y0 = imp_utils.val2pix(implane.header, 0, 0, "D") plt.plot(x0, y0, "ro") plt.gca().set_aspect(1) @@ -828,7 +825,7 @@ def test_header_does_not_contain_negative_naxis_keywords(self): # -class TestAddTableToImageHDU: +class TestAddTableToImageHDU2: @pytest.mark.parametrize("xpix, ypix, value", [(51, 51, 2), (48, 51, 3), @@ -930,69 +927,3 @@ def test_initialises_with_header_with_hdu(self, image_hdu_square, def test_throws_error_if_header_does_not_have_valid_wcs(self): with pytest.raises(ValueError): opt_imp.ImagePlane(fits.Header()) - - -class TestImagePlaneAdd: - def test_simple_add_imagehdu_conserves_flux(self, image_hdu_square, - image_hdu_rect): - fields = [image_hdu_rect, image_hdu_square] - hdr = imp_utils.get_canvas_header(pixel_scale=1 * u.arcsec, - hdu_or_table_list=fields) - - orig_sum = image_hdu_rect.data.sum() - - print(wcs.WCS(image_hdu_rect)) - print(wcs.WCS(hdr)) - - implane = opt_imp.ImagePlane(hdr) - implane.add(image_hdu_rect) - - if PLOTS: - plt.imshow(image_hdu_rect.data) - x, y = wcs.WCS(image_hdu_rect).wcs_world2pix(0, 0, 1) - print(x, y) - plt.plot(x, y, "ro") - plt.show() - - plt.imshow(implane.data) - x, y = wcs.WCS(image_hdu_rect).wcs_world2pix(0, 0, 1) - print(x, y) - plt.plot(x, y, "ro") - plt.show() - - assert np.sum(implane.data) == approx(orig_sum, rel=1e-2) - - # TODO: rm this test once deprecation is complete - @pytest.mark.filterwarnings("ignore:Adding a table directly*:DeprecationWarning") - def test_simple_add_table_conserves_flux(self, image_hdu_rect): - x = [75, -75]*u.arcsec - y = [0, 0]*u.arcsec - flux = [30, 20] * u.Unit("ph s-1") - tbl = Table(names=["x", "y", "flux"], data=[x, y, flux]) - - hdr = imp_utils.get_canvas_header(pixel_scale=0.1 * u.arcsec, - hdu_or_table_list=[image_hdu_rect, - tbl]) - implane = opt_imp.ImagePlane(hdr) - implane.add(tbl) - assert np.isclose(np.sum(implane.data), np.sum(flux.value)) - - # TODO: rm this test once deprecation is complete - @pytest.mark.filterwarnings("ignore:Adding a table directly*:DeprecationWarning") - def test_compound_add_image_and_table_conserves_flux(self, image_hdu_rect): - x = [75, -75]*u.arcsec - y = [0, 0]*u.arcsec - flux = [30, 20] * u.Unit("ph s-1") - tbl = Table(names=["x", "y", "flux"], data=[x, y, flux]) - - hdr = imp_utils.get_canvas_header(pixel_scale=0.1 * u.arcsec, - hdu_or_table_list=[image_hdu_rect, - tbl]) - in_sum = np.sum(flux.value) + np.sum(image_hdu_rect.data) - - implane = opt_imp.ImagePlane(hdr) - implane.add(tbl) - implane.add(image_hdu_rect) - out_sum = np.sum(implane.data) - - assert out_sum == approx(in_sum, rel=5e-3) diff --git a/scopesim/tests/tests_optics/test_ImagePlaneUtils.py b/scopesim/tests/tests_optics/test_ImagePlaneUtils.py index 787442e8a..8658dd423 100644 --- a/scopesim/tests/tests_optics/test_ImagePlaneUtils.py +++ b/scopesim/tests/tests_optics/test_ImagePlaneUtils.py @@ -152,6 +152,8 @@ def test_larger_hdu_is_fully_outside_smaller_hdu(self): assert np.sum(new.data) == small_sum + # face is downloaded from the internet. + @pytest.mark.webtest def test_python_image_coords(self): # numpy uses a system of im[y, x] # numpy.shape[0] = y_len, numpy.shape[1] = x_len diff --git a/scopesim/tests/tests_server/test_database.py b/scopesim/tests/tests_server/test_database.py index efb6858f0..55c670f70 100644 --- a/scopesim/tests/tests_server/test_database.py +++ b/scopesim/tests/tests_server/test_database.py @@ -8,7 +8,6 @@ from scopesim.server import ServerError from scopesim.server import database as db from scopesim.server import example_data_utils as dbex -from scopesim.server import github_utils as dbgh @pytest.fixture(scope="class") @@ -136,57 +135,6 @@ def test_downloads_specific_package(self): assert version_dict["version"] == release - @pytest.mark.webtest(github=True) - @pytest.mark.filterwarnings("ignore:Downloading IRDB packages:FutureWarning") - @pytest.mark.filterwarnings("ignore:The function*:DeprecationWarning") - def test_downloads_github_version_of_package_with_semicolon(self): - release = "github:728761fc76adb548696205139e4e9a4260401dfc" - with TemporaryDirectory() as tmpdir: - db.download_packages("ELT", release=release, - save_dir=tmpdir) - filename = Path(tmpdir, "ELT", "EC_sky_25.tbl") - - assert filename.exists() - - @pytest.mark.webtest(github=True) - @pytest.mark.filterwarnings("ignore:Downloading IRDB packages:FutureWarning") - @pytest.mark.filterwarnings("ignore:The function*:DeprecationWarning") - def test_downloads_github_version_of_package_with_at_symbol(self): - release = "github@728761fc76adb548696205139e4e9a4260401dfc" - with TemporaryDirectory() as tmpdir: - db.download_packages("ELT", release=release, - save_dir=tmpdir) - filename = Path(tmpdir, "ELT", "EC_sky_25.tbl") - - assert filename.exists() - - -@pytest.mark.webtest(github=True) -@pytest.mark.filterwarnings("ignore:The function*:DeprecationWarning") -class TestDownloadGithubFolder: - def test_downloads_current_package(self): - with TemporaryDirectory() as tmpdir: - # tmpdir = "." - url = "https://github.com/AstarVienna/irdb/tree/dev_master/MICADO" - dbgh.download_github_folder(url, output_dir=tmpdir) - filename = Path(tmpdir, "MICADO", "default.yaml") - - assert filename.exists() - - def test_downloads_with_old_commit_hash(self): - with TemporaryDirectory() as tmpdir: - url = "https://github.com/AstarVienna/irdb/tree/728761fc76adb548696205139e4e9a4260401dfc/ELT" - dbgh.download_github_folder(url, output_dir=tmpdir) - filename = Path(tmpdir, "ELT", "EC_sky_25.tbl") - - assert filename.exists() - - def test_throws_for_bad_url(self): - with TemporaryDirectory() as tmpdir: - url = "https://github.com/AstarVienna/irdb/tree/bogus/MICADO" - with pytest.raises(ServerError): - dbgh.download_github_folder(url, output_dir=tmpdir) - def test_registry_files(): registry = (Path(__file__).parent.parent.parent / diff --git a/scopesim/tests/tests_source/test_source_Source.py b/scopesim/tests/tests_source/test_source_Source.py index b7da0cf42..feea28e44 100644 --- a/scopesim/tests/tests_source/test_source_Source.py +++ b/scopesim/tests/tests_source/test_source_Source.py @@ -262,14 +262,20 @@ def test_empty_source_is_the_additive_identity(self, image_source): class TestSourceImageInRange: - # TODO: figure out why this occurs here, then solve it - @pytest.mark.filterwarnings("ignore:Adding a table directly*:DeprecationWarning") + @pytest.mark.skip(reason=( + "This was the only place where the 'add table to ImagePlane' branch " + "was still used. Figure out what actually gets tested here and then " + "try to do it in another way." + )) def test_returns_an_image_plane_object(self, table_source): im = table_source.image_in_range(1*u.um, 2*u.um) assert isinstance(im, ImagePlane) - # TODO: figure out why this occurs here, then solve it - @pytest.mark.filterwarnings("ignore:Adding a table directly*:DeprecationWarning") + @pytest.mark.skip(reason=( + "This was the only place where the 'add table to ImagePlane' branch " + "was still used. Figure out what actually gets tested here and then " + "try to do it in another way." + )) def test_flux_from_table_on_image_is_as_expected(self, table_source): ph = table_source.photons_in_range(1*u.um, 2*u.um) ref = table_source.fields[0].field["ref"] diff --git a/scopesim/utils.py b/scopesim/utils.py index c8db463d6..a22c19ae7 100644 --- a/scopesim/utils.py +++ b/scopesim/utils.py @@ -15,6 +15,7 @@ from docutils.core import publish_string import yaml import numpy as np +from numpy.typing import NDArray import matplotlib as mpl from matplotlib import pyplot as plt from astropy import units as u @@ -350,7 +351,7 @@ def zeros_from_header( header: fits.Header, dtype: type | np.dtype = float, ndims: int | None = None, -) -> np.ndarray: +) -> NDArray: """ Create all-zero array of the shape given by NAXISn keywords in `header`. @@ -369,7 +370,7 @@ def zeros_from_header( Returns ------- - arr : np.ndarray + arr : NDArray All-zero array of desired shape and dtype. """ @@ -715,7 +716,7 @@ def write_report(text, filename=None, output=None): for fmt in output: out_text = deepcopy(text) if fmt.lower() == "latex": - out_text = publish_string(out_text, writer_name="latex") + out_text = publish_string(out_text, writer="latex") out_text = out_text.decode("utf-8") suffix = {"rst": ".rst", "latex": ".tex"}[fmt]