diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..60cc45ec --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.dockerignore +.venv diff --git a/.github/ISSUE_TEMPLATE/improvement-task.md b/.github/ISSUE_TEMPLATE/improvement-task.md new file mode 100644 index 00000000..956fcc60 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/improvement-task.md @@ -0,0 +1,20 @@ +--- +name: Improvement Task +about: Suggest an idea for this project +title: "[TASK]" +labels: '' +assignees: felipe-mf, VirginiaBee47 + +--- + +# Task Title + +Description: Task Description + + +Measurement Criteria: +- Criterion #1 +- Criterion #2 + +Timeline: +- List dates of past progress and planned future updates. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..c217fed9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,57 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: 'New version' + required: true + +jobs: + update-and-build: + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v2 + + - name: Update pyproject.toml version + run: | + sed -i "s/^version = .*/version = \"${{ github.event.inputs.version }}\"/" pyproject.toml + git config --global user.name 'GitHub Actions' + git config --global user.email 'actions@github.com' + git commit -am "ci: Version bump ${{ github.event.inputs.version }}" + git push + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ vars.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build and push Docker Image + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64 #,linux/arm64 future ;) + push: true + tags: ${{ vars.DOCKER_IMAGE_NAME }}:${{ github.event.inputs.version }}, ${{ vars.DOCKER_IMAGE_NAME }}:latest + + - name: Push version bump commit + run: | + git tag ${{ github.event.inputs.version }} + git push origin ${{ github.event.inputs.version }} + git push + + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.event.inputs.version }} + release_name: Release ${{ github.event.inputs.version }} + draft: false + prerelease: false diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 748e882f..335214b2 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ tags data/* pythia/__pycache__/* +pythia/.idea/* .idea/* .vscode/* *.pyc @@ -11,3 +12,8 @@ work/* docs/source/_build docs/build .venv +dist +*~ +.python-version +.idea/ +pythia-data/ \ No newline at end of file diff --git a/.python-version b/.python-version deleted file mode 100644 index c77a7de8..00000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.7.9 diff --git a/Dockerfile b/Dockerfile index 47282c12..6a7129e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,39 +1,27 @@ -FROM dssat/dssat-csm +FROM dssat/dssat-csm:v4.8.2.0 -COPY . /app/pythia -RUN ln -sf /bin/bash /bin/sh && \ -# install pre-reqs for pyenv installed pythons -apt-get install -y build-essential libssl-dev zlib1g-dev libbz2-dev \ -libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \ -xz-utils tk-dev libffi-dev liblzma-dev python-openssl git libspatialindex-dev && \ -# setup pyenv -curl https://pyenv.run | bash && \ -echo 'export PATH="/root/.pyenv/bin:/root/.local/bin:$PATH"' >> ~/.bashrc && \ -echo 'eval "$(pyenv init -)"' >> ~/.bashrc && \ -echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc && \ -export PATH="/root/.pyenv/bin:/root/.local/bin:$PATH" && \ -eval "$(pyenv init -)" && \ -eval "$(pyenv virtualenv-init i)" && \ -# install python 3.7.9 -pyenv install 3.7.9 && \ -pyenv rehash && \ -pyenv virtualenv 3.7.9 pythia-3.7.9 && \ -pyenv activate pythia-3.7.9 && \ -pip install --upgrade pip && \ -pip install pipenv && \ -# install dependencies -cd /app/pythia && \ -pipenv install && \ -echo "#!/bin/bash" > /app/pythia.sh && \ -echo "" >> /app/pythia.sh && \ -echo 'export PATH="/root/.pyenv/bin:/root/.local/bin:$PATH"' >> /app/pythia.sh && \ -echo 'export PYENV_VIRTUALENV_DISABLE_PROMPT=1' >> /app/pythia.sh && \ -echo 'eval "$(pyenv init -)"' >> /app/pythia.sh && \ -echo 'eval "$(pyenv virtualenv-init -)"' >> /app/pythia.sh && \ -echo "pyenv activate pythia-3.7.9" >> /app/pythia.sh && \ -echo "python /app/pythia/pythia.py \$@" >> /app/pythia.sh && \ -echo "pyenv deactivate" && \ -chmod 755 /app/pythia.sh +RUN apt-get update && apt-get install -y \ + python3.11 \ + python3.11-dev \ + python3-pip \ + python3-venv \ + python3-poetry \ + python3-virtualenv \ + curl \ + gdal-bin=3.6.2+dfsg-1+b2 \ + libgdal-dev=3.6.2+dfsg-1+b2 \ + && rm -rf /var/lib/apt/lists/* -ENTRYPOINT ["/app/pythia.sh"] -CMD ["-h"] +ENV GDAL_VERSION 3.6.2 +ENV C_INCLUDE_PATH=/usr/include/python3.11/cpython +ENV CPLUS_INCLUDE_PATH=/usr/include/python3.11/cpython + +WORKDIR /app/pythia + +COPY pyproject.toml poetry.toml poetry.lock ./ +RUN POETRY_VIRTUALENVS_CREATE=false poetry install --no-interaction --no-ansi + +COPY . ./ +ENV PATH="${PATH}:/app/pythia/bin" + +ENTRYPOINT ["pythia"] diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..a495892d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2025, DSSAT Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..cb65e556 --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ +# DSSAT-Pythia Installation Guide + +## Software Requirements +To install and run DSSAT-Pythia on your PC, you will need the following software: + +1. **DSSAT**: + Download and install DSSAT from [DSSAT website](https://get.dssat.net/request/?sft=4). + +2. **Python 3.8**: + Download the Python 3.8 installer for Windows or macOS from [Python 3.8 release page](https://www.python.org/downloads/release/python-389/). + - During installation, select “Customize installation.” + - Select all options under “Optional Features” and “Advanced Options.” + - Note: DSSAT-Pythia only works with Python 3.8. If you have another version, uninstall it and download Python 3.8 from the above link. + +3. **Git**: + Download and install Git from [Git download page](https://git-scm.com/download/win). + +4. **Community Version of Visual Studio**: + Download and install Visual Studio from [Visual Studio website](https://visualstudio.microsoft.com/downloads/). + Select the **Desktop development with C++** workload during installation. + +5. **RStudio**: + To use RStudio on your PC, install both R and RStudio: + - Download and install R from [CRAN website](https://cran.rstudio.com/). + - Download and install RStudio from [RStudio website](https://posit.co/download/rstudio-desktop/). + +--- + + +## Steps to Install DSSAT-Pythia on PC + +1. Enable **Developer Mode**: + - Open Windows Settings > Update & Security > For Developers. + - Switch on Developer Mode and restart the computer. + +2. Open the **Command Prompt** in the C Drive: + - Open the C drive and type `cmd` in the address bar and press Enter. + +3. Clone the DSSAT-Pythia repository: + ```bash + git clone https://github.com/dssat/pythia.git pythia + ``` + +4. Navigate to the cloned directory: + ```bash + cd pythia + ``` + +5. Delete the `poetry.lock` file: + ```bash + del poetry.lock + ``` + +6. Install Poetry: + ```bash + pip install poetry + ``` + +7. Install DSSAT-Pythia: + ```bash + \poetry install + \poetry build + ``` + - On Windows, poetry will be found in "C:\Users\username\AppData\Local\Programs\Python\Python38\Scripts" + +8. Install the Pythia wheel file: + - Navigate to the `dist` folder and install the `.whl` file: + ```bash + cd dist + pip install pythia-2.3.0-py3-none-any.whl + ``` + - **Note**: Check the version in the `dist` folder and adjust the command if necessary. + +9. Add the path to `pythia.exe` to your environment variables. + +10. Close the command prompt. + +--- + +## Troubleshooting + +- If you encounter any issues during installation, delete the folder `C:\pythia` and repeat the installation steps. + +--- + +## Input Files Setup + +1. Download the `InputFiles.zip` folder from [Google Drive link](https://drive.google.com/file/d/1vlBeWEavNggcuhRMgmO79aHTYZ7aq_Im/view?usp=sharing), unzip it, and save the `Simulation_Data` folder in `C:\pythia\`. + +2. Open the folder `C:\pythia\Simulation_Data\OUTPUT\Sri_Lanka` and remove all folders (if any). + + **Note**: Remove the contents from the `OUTPUT` folder every time you run the model. + +--- + +## Running the Model + +1. Open the command prompt at `C:\pythia\Simulation_Data\Sri_Lanka` by typing `cmd` in the folder address bar. + +2. Run the following commands to simulate maize and rice: + ```bash + pythia --all C:/pythia/Simulation_Data/Sri_Lanka/SL_Maize.json + pythia --all C:/pythia/Simulation_Data/Sri_Lanka/SL_Rice.json + ``` + +3. To view the output: + - Open the `.json` file in `C:\pythia\Simulation_Data\Sri_Lanka\`. + - Find the working directory in `"workDir": "C:/pythia/Simulation_Data/OUTPUT/Sri_Lanka/…"` and navigate to that folder. + - The output will be available in `.csv` format. + +--- + +## Plotting Output in RStudio + +1. Open the R code file: `C:\pythia\Simulation_Data\OUTPUT\ACASA_Sri_Lanka_maize.R`. + +2. Install all required packages and modify the file location on line 22 to match the output `.csv` file. + +3. Run the R script to generate the yield plot. + +4. The plot will be saved at the location specified in line 62. + + **Note**: If you encounter errors reading the `.csv` file, open the file and delete all columns except `LATITUDE`, `LONGITUDE`, and `HWAH`. diff --git a/bin/pythia b/bin/pythia new file mode 100755 index 00000000..1d17ae7c --- /dev/null +++ b/bin/pythia @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +source .venv/bin/activate +python3 -m pythia $@ diff --git a/default.nix b/default.nix deleted file mode 100644 index 5af02898..00000000 --- a/default.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ pkgs ? import {} }: - -let - pp = pkgs.python37Packages; -in pkgs.mkShell rec { - name = "pythiaEnv"; - venvDir = "./.venv"; - buildInputs = [ - pkgs.python37 - pkgs.pipenv - pp.venvShellHook - - pkgs.libspatialite - pkgs.libspatialindex - ]; - - preShellHook = '' - echo "${pkgs.libspatialindex}/lib" - export DYLD_FALLBACK_LIBRARY_PATH="${pkgs.libspatialindex}/lib" - ''; - postVenvCreation = '' - unset SOURCE_DATE_EPOCH - pipenv install - ''; - - postShellHook = '' - unset SOURCE_DATE_EPOCH - ''; -} diff --git a/develop.nix b/develop.nix deleted file mode 100644 index 97cf2c9c..00000000 --- a/develop.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ pkgs ? import {} }: - -let - pp = pkgs.python37Packages; -in pkgs.mkShell rec { - name = "pythiaEnv"; - venvDir = "./.venv"; - buildInputs = [ - pkgs.python37 - pkgs.pipenv - pp.venvShellHook - pp.poetry - - pkgs.libspatialite - pkgs.libspatialindex - - pkgs.black - pp.bandit - ]; - - preShellHook = '' - echo "${pkgs.libspatialindex}/lib" - export DYLD_FALLBACK_LIBRARY_PATH="${pkgs.libspatialindex}/lib" - ''; - postVenvCreation = '' - unset SOURCE_DATE_EPOCH - pipenv install - ''; - - postShellHook = '' - unset SOURCE_DATE_EPOCH - ''; -} diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 00000000..a9827a69 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,26 @@ +# Pythia - Release Guide + +## Versioning + +When it comes to managing software releases for Pythia, understanding how to properly version each release is crucial. Pythia follows the principles of [semantic versioning](https://semver.org/), a widely adopted system designed to give meaning to version numbers. In semantic versioning, a version number is made up of three parts: ``MAJOR.MINOR.PATCH``, with each part serving a specific purpose: + + - **MAJOR** version when you make incompatible API changes, + - **MINOR** version when you add functionality in a backwards compatible manner, and + - **PATCH** version when you make backwards compatible bug fixes. + +This system helps developers and users understand the scope and impact of each release. Here's a breakdown of when to increment each part of the version number: + + - Increment the **MAJOR** version when there are significant changes that break backward compatibility with the previous versions. This could include changes to the software's architecture, removal of functionalities, or any other changes that would require users to make modifications to their existing setups. + - Increment the **MINOR** version when new features or functionalities are added in a backward-compatible manner. This means that the new version adds value to the software without disrupting the existing functionalities or forcing changes in the users' setups. + - Increment the **PATCH** version for minor changes that fix bugs without adding new features or changing existing ones. These should not affect the software's functionality beyond the specific fixes. + +## Release Process +Pythia is released through GitHub Actions. Once a release is triggered, the CI/CD pipeline will automatically build the software and publish a Docker image to Dockerhub. A GitHub release is also created. + +In order to deploy new versions of Pythia, go to "([Actions > Release](https://github.com/DSSAT/pythia/actions/workflows/release.yml) > Run Workflow > fill in the version number, follow [versioning](#versioning) to figure out this information > Run workflow). + +![image](https://github.com/DSSAT/pythia/assets/18128642/a5db0435-9d17-4be8-82c8-54adcbcf860d) + +> **IMPORTANT:** The version number must strictly match what is described in [versioning](#versioning). **Do not** add any kind of prefix or suffix that is not semantically valid. + +> **IMPORTANT:** The version number on ``pyproject.toml`` is automatically updated by the CI/CD pipeline. **Do not** change it manually. diff --git a/docs/about.rst b/docs/about.rst index 35d22ba2..1e97e348 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -1,7 +1,7 @@ What is Pythia? =============== -Pythia is a simple framework to assist users in running DSSAT over a spatial area based on grid points. Pythia can use any DSSAT v4.7 build. The inputs to the framework are: +Pythia is a simple framework to assist users in running DSSAT over a spatial area based on grid points. Pythia can use any DSSAT v4.7 build or greater. The inputs to the framework are: - A JSON configuration file - Shape files diff --git a/docs/conf.py b/docs/conf.py index 104252c8..d38a504b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,12 +17,12 @@ # -- Project information ----------------------------------------------------- -project = 'Pythia' -copyright = '2020, DSSAT Foundation' -author = 'Christopher Villalobos' +project = "Pythia" +copyright = "2020 - 2022, DSSAT Foundation" +author = "Christopher Villalobos" # The full version, including alpha/beta/rc tags -release = '1.0.0' +release = "1.0.0" # -- General configuration --------------------------------------------------- @@ -30,11 +30,10 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ -] +extensions = [] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -47,15 +46,15 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # -- RTD Settings ------------------------------------------------------------- # It seems that RTD needs to have this set in order for the build to occur. # -master_doc = 'index' +master_doc = "index" diff --git a/docs/json.rst b/docs/json.rst index 34ffc4af..57fd22a8 100644 --- a/docs/json.rst +++ b/docs/json.rst @@ -8,13 +8,13 @@ General This is the root of the JSON object. All values in this section are considered to be globally available. name - :Type: no whitespace string + :Type: no whitespace string :Required: true :Description: The name of this configuration. - + workDir - :Type: directory string + :Type: directory string :Required: true :Description: The root output directory of this run. All DSSAT files will generated under this directory. @@ -32,13 +32,13 @@ cores :Type: positive integer :Default value: number of logical CPUs available. :Description: The number of cores to be used by pythia. Cores are used to execute the model. - + threads :Type: positive integer :Default value: number of cores available / 2 - :Description: The number of threads to be used by pythia. This is used for I/O work. - + :Description: The number of threads to be used by pythia. This is used for I/O work. + sample :Type: positive integer :Description: Used to subset the data. Applies the configuration the first *x* number of valid simulations. This may be different between runs. @@ -72,8 +72,6 @@ sites {"sites": [[29.6340239,-82.3631502]]} - - startYear :Type: 4-digit year :Required: true @@ -84,21 +82,25 @@ Supported DSSAT variables The following are the supported DSSAT variables. To add more variables, please extend ``template.py``. +-------+---------+-------+-------+ -| cname | icbl | pdate | sno3 | +| cname | nyers | pdate | irrig | ++-------+---------+-------+-------+ +| erain | flhst | pfrst | wsta | +-------+---------+-------+-------+ -| erain | icren | pfrst | wsta | +| famn | fhdur | ph2ol | xcrd | +-------+---------+-------+-------+ -| famn | icres | ph2ol | xcrd | +| fdap | sdate | plast | ycrd | +-------+---------+-------+-------+ -| fdap | icrt | plast | ycrd | +| fdate | id_soil | ramt | ingeno| +-------+---------+-------+-------+ -| fdate | id_soil | ramt | | +| fdate | icbl | sh2o | snh4 | +-------+---------+-------+-------+ -| fdate | ingeno | sdate | | +| sno3 | icrt | icnr | icrn | +-------+---------+-------+-------+ -| fhdur | irrig | sh2o | | +| icre | icwd | icres | icren | +-------+---------+-------+-------+ -| flhst | nyers | snh4 | | +| icrep | icrip | icrid | | ++-------+---------+-------+-------+ +| sadat | smhb | smpx | smke | +-------+---------+-------+-------+ **Note:** All dates are ISO formatted as ``YYYY-MM-DD`` @@ -107,7 +109,7 @@ DSSAT Structures ~~~~~~~~~~~~~~~~ ic_layers - :Type: Array of initial condition layer objects + :Type: Array of initial condition layer objects :Shape: ``[{"icbl": , "sh2o": , "shn4": , "sno3": },...]`` :Helper function: ``generate_ic_layers`` @@ -121,4 +123,4 @@ Arbitrary Variables Useful Functions -~~~~~~~~~~~~~~~~ \ No newline at end of file +~~~~~~~~~~~~~~~~ diff --git a/guide.adoc b/guide.adoc deleted file mode 100644 index 5a9bfb72..00000000 --- a/guide.adoc +++ /dev/null @@ -1,39 +0,0 @@ -= pythia User Guide = -Christopher Villalobos -:toc: -:icons: - -== pythia Stages == -1. Setup -2. Execution -3. Analysis - - -== pythia Analysis == -Below is a sample of the analysis sections of the pythia configuration file. - -[source,json] -{ - "analysis_default": { - "per_pixel_prefix": "pp" - }, - "analysis": [ - { - "title": "Yield without scaling", - "filename_format": "yields-$run-$ts" // automatically append .tif - "output_type": "tiff", - "runs": ["each::", "combine::all_rf::a+b+c", "combine::all::a+b+c+d"], - "timeseries": { - "variable":"PDAT", - "timestep":"Y" //Y=Yearly, M=Monthly, D=Daily - "as": "year" - }, - "variables": [ - { - "variable": "HWAM", - "as": "Yield" - } - ] - } - ] -} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index a86897f1..4a6a296c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,258 +1,1141 @@ +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. + [[package]] name = "affine" -version = "2.3.0" -description = "Matrices describing affine transformation of the plane." -category = "main" +version = "2.4.0" +description = "Matrices describing affine transformation of the plane" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "affine-2.4.0-py3-none-any.whl", hash = "sha256:8a3df80e2b2378aef598a83c1392efd47967afec4242021a0b06b4c7cbc61a92"}, + {file = "affine-2.4.0.tar.gz", hash = "sha256:a24d818d6a836c131976d22f8c27b8d3ca32d0af64c1d8d29deb7bafa4da1eea"}, +] + +[package.extras] +dev = ["coveralls", "flake8", "pydocstyle"] +test = ["pytest (>=4.6)", "pytest-cov"] + +[[package]] +name = "appnope" +version = "0.1.3" +description = "Disable App Nap on macOS >= 10.9" optional = false python-versions = "*" +groups = ["dev"] +markers = "sys_platform == \"darwin\"" +files = [ + {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, + {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, +] + +[[package]] +name = "astroid" +version = "2.6.6" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = "~=3.6" +groups = ["dev"] +files = [ + {file = "astroid-2.6.6-py3-none-any.whl", hash = "sha256:ab7f36e8a78b8e54a62028ba6beef7561db4cdb6f2a5009ecc44a6f42b5697ef"}, + {file = "astroid-2.6.6.tar.gz", hash = "sha256:3975a0bd5373bdce166e60c851cfcbaf21ee96de80ec518c1f4cb3e94c3fb334"}, +] + +[package.dependencies] +lazy-object-proxy = ">=1.4.0" +setuptools = ">=20.0" +wrapt = ">=1.11,<1.13" + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" [package.extras] -test = ["pytest (>=3.0)", "pytest-cov", "pydocstyle", "coveralls"] +astroid = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\""] +test = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\"", "pytest"] [[package]] name = "atomicwrites" -version = "1.4.0" +version = "1.4.1" description = "Atomic file writes." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +] [[package]] name = "attrs" -version = "20.3.0" +version = "23.2.0" description = "Classes Without Boilerplate" -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6) ; platform_python_implementation == \"CPython\" and python_version >= \"3.8\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.8\""] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "1.5.7" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "autopep8-1.5.7-py2.py3-none-any.whl", hash = "sha256:aa213493c30dcdac99537249ee65b24af0b2c29f2e83cd8b3f68760441ed0db9"}, + {file = "autopep8-1.5.7.tar.gz", hash = "sha256:276ced7e9e3cb22e5d7c14748384a5cf5d9002257c0ed50c0e075b68011bb6d0"}, +] + +[package.dependencies] +pycodestyle = ">=2.7.0" +toml = "*" + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + +[[package]] +name = "black" +version = "22.12.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2020.12.5" +version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, +] [[package]] name = "click" -version = "7.1.2" +version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "click-plugins" version = "1.1.1" description = "An extension module for click to enable registering CLI commands via setuptools entry-points." -category = "main" optional = false python-versions = "*" +groups = ["main"] +files = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] [package.dependencies] click = ">=4.0" [package.extras] -dev = ["pytest (>=3.6)", "pytest-cov", "wheel", "coveralls"] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] [[package]] name = "cligj" -version = "0.7.1" +version = "0.7.2" description = "Click params for commmand line interfaces to GeoJSON" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <4" +groups = ["main"] +files = [ + {file = "cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df"}, + {file = "cligj-0.7.2.tar.gz", hash = "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27"}, +] [package.dependencies] -click = ">=4.0,<8" +click = ">=4.0" [package.extras] test = ["pytest-cov"] [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] [[package]] name = "fiona" -version = "1.8.18" +version = "1.9.5" description = "Fiona reads and writes spatial data files" -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "fiona-1.9.5-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:5f40a40529ecfca5294260316cf987a0420c77a2f0cf0849f529d1afbccd093e"}, + {file = "fiona-1.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:374efe749143ecb5cfdd79b585d83917d2bf8ecfbfc6953c819586b336ce9c63"}, + {file = "fiona-1.9.5-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:35dae4b0308eb44617cdc4461ceb91f891d944fdebbcba5479efe524ec5db8de"}, + {file = "fiona-1.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:5b4c6a3df53bee8f85bb46685562b21b43346be1fe96419f18f70fa1ab8c561c"}, + {file = "fiona-1.9.5-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:6ad04c1877b9fd742871b11965606c6a52f40706f56a48d66a87cc3073943828"}, + {file = "fiona-1.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fb9a24a8046c724787719e20557141b33049466145fc3e665764ac7caf5748c"}, + {file = "fiona-1.9.5-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:d722d7f01a66f4ab6cd08d156df3fdb92f0669cf5f8708ddcb209352f416f241"}, + {file = "fiona-1.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:7ede8ddc798f3d447536080c6db9a5fb73733ad8bdb190cb65eed4e289dd4c50"}, + {file = "fiona-1.9.5-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:8b098054a27c12afac4f819f98cb4d4bf2db9853f70b0c588d7d97d26e128c39"}, + {file = "fiona-1.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d9f29e9bcbb33232ff7fa98b4a3c2234db910c1dc6c4147fc36c0b8b930f2e0"}, + {file = "fiona-1.9.5-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f1af08da4ecea5036cb81c9131946be4404245d1b434b5b24fd3871a1d4030d9"}, + {file = "fiona-1.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:c521e1135c78dec0d7774303e5a1b4c62e0efb0e602bb8f167550ef95e0a2691"}, + {file = "fiona-1.9.5-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:fce4b1dd98810cabccdaa1828430c7402d283295c2ae31bea4f34188ea9e88d7"}, + {file = "fiona-1.9.5-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:3ea04ec2d8c57b5f81a31200fb352cb3242aa106fc3e328963f30ffbdf0ff7c8"}, + {file = "fiona-1.9.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4877cc745d9e82b12b3eafce3719db75759c27bd8a695521202135b36b58c2e7"}, + {file = "fiona-1.9.5-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:ac2c250f509ec19fad7959d75b531984776517ef3c1222d1cc5b4f962825880b"}, + {file = "fiona-1.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4df21906235928faad856c288cfea0298e9647f09c9a69a230535cbc8eadfa21"}, + {file = "fiona-1.9.5-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:81d502369493687746cb8d3cd77e5ada4447fb71d513721c9a1826e4fb32b23a"}, + {file = "fiona-1.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:ce3b29230ef70947ead4e701f3f82be81082b7f37fd4899009b1445cc8fc276a"}, + {file = "fiona-1.9.5-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:8b53ce8de773fcd5e2e102e833c8c58479edd8796a522f3d83ef9e08b62bfeea"}, + {file = "fiona-1.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd2355e859a1cd24a3e485c6dc5003129f27a2051629def70036535ffa7e16a4"}, + {file = "fiona-1.9.5-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:9a2da52f865db1aff0eaf41cdd4c87a7c079b3996514e8e7a1ca38457309e825"}, + {file = "fiona-1.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:cfef6db5b779d463298b1113b50daa6c5b55f26f834dc9e37752116fa17277c1"}, + {file = "fiona-1.9.5.tar.gz", hash = "sha256:99e2604332caa7692855c2ae6ed91e1fffdf9b59449aa8032dd18e070e59a2f7"}, +] [package.dependencies] -attrs = ">=17" +attrs = ">=19.2.0" certifi = "*" -click = ">=4.0,<8" +click = ">=8.0,<9.0" click-plugins = ">=1.0" cligj = ">=0.5" -munch = "*" -six = ">=1.7" +importlib-metadata = {version = "*", markers = "python_version < \"3.10\""} +setuptools = "*" +six = "*" [package.extras] -all = ["pytest (>=3)", "boto3 (>=1.2.4)", "pytest-cov", "shapely", "mock"] +all = ["Fiona[calc,s3,test]"] calc = ["shapely"] -s3 = ["boto3 (>=1.2.4)"] -test = ["pytest (>=3)", "pytest-cov", "boto3 (>=1.2.4)", "mock"] +s3 = ["boto3 (>=1.3.1)"] +test = ["Fiona[s3]", "pytest (>=7)", "pytest-cov", "pytz"] + +[[package]] +name = "flake8" +version = "3.9.2" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +groups = ["dev"] +files = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] [[package]] name = "importlib-metadata" -version = "3.3.0" +version = "7.0.1" description = "Read metadata from Python packages" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, +] +markers = {main = "python_version < \"3.10\""} [package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[package]] +name = "ipython" +version = "8.12.3" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, + {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +groups = ["dev"] +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jedi" +version = "0.18.2" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, + {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, +] + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] name = "jinja2" -version = "2.11.2" +version = "3.1.3" description = "A very fast and expressive template engine." -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] [package.dependencies] -MarkupSafe = ">=0.23" +MarkupSafe = ">=2.0" [package.extras] -i18n = ["Babel (>=0.8)"] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "lazy-object-proxy" +version = "1.10.0" +description = "A fast and thorough lazy object proxy." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, + {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, +] [[package]] name = "markupsafe" -version = "1.1.1" +version = "2.1.4" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] [[package]] name = "more-itertools" -version = "8.6.0" +version = "10.2.0" description = "More routines for operating on iterables, beyond itertools" -category = "dev" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, + {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, +] + +[[package]] +name = "msgpack" +version = "1.0.7" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"}, + {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"}, + {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"}, + {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"}, + {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"}, + {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"}, + {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"}, + {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"}, + {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"}, + {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"}, + {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"}, + {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"}, + {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"}, + {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] [[package]] -name = "munch" -version = "2.5.0" -description = "A dot-accessible dictionary (a la JavaScript objects)" -category = "main" +name = "neovim" +version = "0.3.1" +description = "Transition packgage for pynvim" optional = false python-versions = "*" +groups = ["dev"] +files = [ + {file = "neovim-0.3.1.tar.gz", hash = "sha256:a6a0e7a5b4433bf4e6ddcbc5c5ff44170be7d84259d002b8e8d8fb4ee78af60f"}, +] [package.dependencies] -six = "*" - -[package.extras] -testing = ["pytest", "coverage", "astroid (>=1.5.3,<1.6.0)", "pylint (>=1.7.2,<1.8.0)", "astroid (>=2.0)", "pylint (>=2.3.1,<2.4.0)"] -yaml = ["PyYAML (>=5.1.0)"] +pynvim = ">=0.3.1" [[package]] name = "numpy" -version = "1.19.4" -description = "NumPy is the fundamental package for array computing with Python." -category = "main" +version = "1.24.4" +description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +] [[package]] name = "packaging" -version = "20.8" +version = "23.2" description = "Core utilities for Python packages" -category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] -[package.dependencies] -pyparsing = ">=2.0.2" +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] [[package]] -name = "pandas" -version = "0.25.3" -description = "Powerful data structures for data analysis, time series, and statistics" -category = "main" +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.5.3" +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +groups = ["dev"] +markers = "sys_platform != \"win32\"" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] [package.dependencies] -numpy = ">=1.13.3" -python-dateutil = ">=2.6.1" -pytz = ">=2017.2" +ptyprocess = ">=0.5" -[package.extras] -test = ["pytest (>=4.0.2)", "pytest-xdist", "hypothesis (>=3.58)"] +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] [[package]] -name = "pillow" -version = "5.4.1" -description = "Python Imaging Library (Fork)" -category = "main" +name = "platformdirs" +version = "4.1.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" version = "0.13.1" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +files = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +groups = ["dev"] +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] [package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +wcwidth = "*" + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +groups = ["dev"] +markers = "sys_platform != \"win32\"" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] [package.extras] -dev = ["pre-commit", "tox"] +tests = ["pytest"] [[package]] name = "py" -version = "1.10.0" +version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["dev"] +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] + +[[package]] +name = "pycodestyle" +version = "2.7.0" +description = "Python style guide checker" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +files = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3) ; python_version < \"3.11\""] + +[[package]] +name = "pyflakes" +version = "2.3.1" +description = "passive checker of Python programs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +files = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata ; python_version < \"3.8\""] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pylint" +version = "2.9.6" +description = "python code static checker" +optional = false +python-versions = "~=3.6" +groups = ["dev"] +files = [ + {file = "pylint-2.9.6-py3-none-any.whl", hash = "sha256:2e1a0eb2e8ab41d6b5dbada87f066492bb1557b12b76c47c2ee8aa8a11186594"}, + {file = "pylint-2.9.6.tar.gz", hash = "sha256:8b838c8983ee1904b2de66cce9d0b96649a91901350e956d78f289c3bc87b48e"}, +] + +[package.dependencies] +astroid = ">=2.6.5,<2.7" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.7" +toml = ">=0.7.1" + +[[package]] +name = "pyls-flake8" +version = "0.4.0" +description = "A Flake8 plugin for the Python Language Server" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "pyls-flake8-0.4.0.tar.gz", hash = "sha256:630cec96e494c0f3bffa381451d71a05f46b4f4e6d9b897ad6377e81cc4f8f70"}, + {file = "pyls_flake8-0.4.0-py2.py3-none-any.whl", hash = "sha256:ca8051e53ee0e06e621abfa7bdc72c9a7f56d5bede51fa7c17caba166b7101a2"}, +] + +[package.dependencies] +flake8 = ">=3.6.0" +python-lsp-server = "*" + +[[package]] +name = "pyls-isort" +version = "0.2.2" +description = "Isort plugin for python-lsp-server" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "pyls-isort-0.2.2.tar.gz", hash = "sha256:2192bd2203db00459f85eb329521feba58af63075d2dd10a051a4eccd000bba0"}, +] + +[package.dependencies] +isort = "*" +python-lsp-server = "*" + +[[package]] +name = "pylsp-rope" +version = "0.1.11" +description = "Extended refactoring capabilities for Python LSP Server using Rope." +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "pylsp-rope-0.1.11.tar.gz", hash = "sha256:48aadf993dafa5e8fca1108b4a5431314cf80bc78cffdd56400ead9c407553be"}, + {file = "pylsp_rope-0.1.11-py3-none-any.whl", hash = "sha256:71fb08dc6ead9a69202f2e7c5f7bbc5ada037a0f0722d7eb2d1f8cac2c9a18af"}, +] + +[package.dependencies] +python-lsp-server = "*" +rope = ">=0.21.0" + +[package.extras] +dev = ["build", "pytest", "twine"] +test = ["pytest"] + +[[package]] +name = "pynvim" +version = "0.5.0" +description = "Python client for Neovim" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "pynvim-0.5.0-py2.py3-none-any.whl", hash = "sha256:2ac197ef0cdfff53719184a45c33cfb7cef88d1c9bf7f0525c21b3239cb5365f"}, + {file = "pynvim-0.5.0.tar.gz", hash = "sha256:e80a11f6f5d194c6a47bea4135b90b55faca24da3544da7cf4a5f7ba8fb09215"}, +] + +[package.dependencies] +greenlet = ">=3.0" +msgpack = ">=0.5.0" + +[package.extras] +pyuv = ["pyuv (>=1.0.0)"] +test = ["pytest"] [[package]] name = "pyparsing" -version = "2.4.7" -description = "Python parsing module" -category = "main" +version = "3.1.1" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.6.8" +groups = ["main"] +files = [ + {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, + {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" version = "5.4.3" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, + {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, +] [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=17.4.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} more-itertools = ">=4.0.0" packaging = "*" pluggy = ">=0.12,<1.0" @@ -264,429 +1147,459 @@ checkqa-mypy = ["mypy (==v0.761)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] -name = "python-dateutil" -version = "2.8.1" -description = "Extensions to the standard Python datetime module" -category = "main" +name = "python-lsp-black" +version = "1.1.0" +description = "Black plugin for the Python LSP Server" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "python-lsp-black-1.1.0.tar.gz", hash = "sha256:4f665aca0fd1611f9e2a4c1ea09de8809810bbe5013eeee5dfb8796c90a12d4c"}, + {file = "python_lsp_black-1.1.0-py3-none-any.whl", hash = "sha256:1d2a9eb1f46c28cb994b22bfd1463a90d4779ad314d12db65d91940b0f1bd137"}, +] [package.dependencies] -six = ">=1.5" +black = ">=19.3b0" +python-lsp-server = "*" +toml = "*" + +[package.extras] +dev = ["flake8", "isort (>=5.0)", "mypy", "pre-commit", "pytest", "types-pkg-resources", "types-setuptools", "types-toml"] [[package]] -name = "pytz" -version = "2020.5" -description = "World timezone definitions, modern and historical" -category = "main" +name = "python-lsp-jsonrpc" +version = "1.1.2" +description = "JSON RPC 2.0 server library" optional = false -python-versions = "*" +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "python-lsp-jsonrpc-1.1.2.tar.gz", hash = "sha256:4688e453eef55cd952bff762c705cedefa12055c0aec17a06f595bcc002cc912"}, + {file = "python_lsp_jsonrpc-1.1.2-py3-none-any.whl", hash = "sha256:7339c2e9630ae98903fdaea1ace8c47fba0484983794d6aafd0bd8989be2b03c"}, +] + +[package.dependencies] +ujson = ">=3.0.0" + +[package.extras] +test = ["coverage", "pycodestyle", "pyflakes", "pylint", "pytest", "pytest-cov"] + +[[package]] +name = "python-lsp-server" +version = "1.2.4" +description = "Python Language Server for the Language Server Protocol" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "python-lsp-server-1.2.4.tar.gz", hash = "sha256:007278c4419339bd3a61ca6d7eb8648ead28b5f1b9eba3b6bae8540046116335"}, + {file = "python_lsp_server-1.2.4-py3-none-any.whl", hash = "sha256:a163907c83ba7bae6b45a9bc9e407b7001fbed7c7561d032a5fd355ae6fae42c"}, +] + +[package.dependencies] +autopep8 = {version = "*", optional = true, markers = "extra == \"all\""} +flake8 = {version = ">=3.8.0,<4.0.0", optional = true, markers = "extra == \"all\""} +jedi = ">=0.17.2,<0.19.0" +mccabe = {version = ">=0.6.0,<0.7.0", optional = true, markers = "extra == \"all\""} +pluggy = "*" +pycodestyle = {version = ">=2.7.0", optional = true, markers = "extra == \"all\""} +pydocstyle = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} +pyflakes = {version = ">=2.3.0,<2.4.0", optional = true, markers = "extra == \"all\""} +pylint = {version = ">=2.5.0,<2.10.0", optional = true, markers = "extra == \"all\""} +python-lsp-jsonrpc = ">=1.0.0" +rope = {version = ">=0.10.5", optional = true, markers = "extra == \"all\""} +setuptools = ">=39.0.0" +ujson = ">=3.0.0" +yapf = {version = "*", optional = true, markers = "extra == \"all\""} + +[package.extras] +all = ["autopep8", "flake8 (>=3.8.0,<4.0.0)", "mccabe (>=0.6.0,<0.7.0)", "pycodestyle (>=2.7.0)", "pydocstyle (>=2.0.0)", "pyflakes (>=2.3.0,<2.4.0)", "pylint (>=2.5.0,<2.10.0)", "rope (>=0.10.5)", "yapf"] +autopep8 = ["autopep8"] +flake8 = ["flake8 (>=3.8.0,<4.0.0)"] +mccabe = ["mccabe (>=0.6.0,<0.7.0)"] +pycodestyle = ["pycodestyle (>=2.7.0)"] +pydocstyle = ["pydocstyle (>=2.0.0)"] +pyflakes = ["pyflakes (>=2.3.0,<2.4.0)"] +pylint = ["pylint (>=2.5.0,<2.10.0)"] +rope = ["rope (>0.10.5)"] +test = ["coverage", "flaky", "matplotlib", "numpy", "pandas", "pylint (>=2.5.0,<2.10.0)", "pyqt5", "pytest", "pytest-cov"] +yapf = ["yapf"] + +[[package]] +name = "pytoolconfig" +version = "1.3.1" +description = "Python tool configuration" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytoolconfig-1.3.1-py3-none-any.whl", hash = "sha256:5d8cea8ae1996938ec3eaf44567bbc5ef1bc900742190c439a44a704d6e1b62b"}, + {file = "pytoolconfig-1.3.1.tar.gz", hash = "sha256:51e6bd1a6f108238ae6aab6a65e5eed5e75d456be1c2bf29b04e5c1e7d7adbae"}, +] + +[package.dependencies] +packaging = ">=23.2" +platformdirs = {version = ">=3.11.0", optional = true, markers = "extra == \"global\""} +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["sphinx (>=7.1.2)", "tabulate (>=0.9.0)"] +gendocs = ["pytoolconfig[doc]", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-rtd-theme (>=2.0.0)"] +global = ["platformdirs (>=3.11.0)"] +validation = ["pydantic (>=2.5.3)"] [[package]] name = "rasterio" -version = "1.1.8" +version = "1.3.9" description = "Fast and direct raster I/O for use with Numpy and SciPy" -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "rasterio-1.3.9-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:04247da9f4002587ac2bec967c3a72f63fc0e6654101c06850bae3d8131b700d"}, + {file = "rasterio-1.3.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c9edce37b70f4cd4be5d3f5d314877e3130aeebb612120405cd28f83fe200865"}, + {file = "rasterio-1.3.9-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:fd6a850a37840ba590ddcf7ff90ba007b1e231b04434d8b4ac5ce0f746ada91a"}, + {file = "rasterio-1.3.9-cp310-cp310-win_amd64.whl", hash = "sha256:0c83156a44f8fda11876ff9f2ff1b602d7e7434447f7d621353f2929cefb1bf1"}, + {file = "rasterio-1.3.9-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:0172dbd80bd9adc105ec2c9bd207dbd5519ea06b438a4d965c6290ae8ed6ff9f"}, + {file = "rasterio-1.3.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ea5b42597d85868ee88c750cc33f2ae729e1b5e3fe28f99071f39e1417bf1c0"}, + {file = "rasterio-1.3.9-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:be9b343bd08245df22115775dc9513c912afb4134d832662fa165d70cb805c34"}, + {file = "rasterio-1.3.9-cp311-cp311-win_amd64.whl", hash = "sha256:06d53e2e0885f039f960beb7c861400b92ea3e0e5abc2c67483fb56b1e5cbc13"}, + {file = "rasterio-1.3.9-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:a34bb9eef67b7896e2dfb39e10ba6372f9894226fb790bd7a46f5748f205b7d8"}, + {file = "rasterio-1.3.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:67b144b9678f9ad4cf5f2c3f455cbc6a7166c0523179249cee8f2e2c57d76c5b"}, + {file = "rasterio-1.3.9-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:99b72fccb702a921f43e56a4507b4cafe2a9196b478b993b98e82ec6851916d7"}, + {file = "rasterio-1.3.9-cp312-cp312-win_amd64.whl", hash = "sha256:6777fad3c31eb3e5da0ccaa28a032ad07c20d003bcd14f8bc13e16ca2f62348c"}, + {file = "rasterio-1.3.9-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:55bb1a2701dd67c1952b261a2ffbabd947a435d4457f13c25092a32ab7a4b36e"}, + {file = "rasterio-1.3.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:726d8e8884359c34f672312171310052d5483af550ef00fb4f2562cc022a6f5a"}, + {file = "rasterio-1.3.9-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:f65879415df188fdc9388ccf2ee01e0659abae370d12518a17b60151e7d04efe"}, + {file = "rasterio-1.3.9-cp38-cp38-win_amd64.whl", hash = "sha256:89771b70ee722c4cc808e2a6139b367bef1a736ecd497b311b3515d78a5d16bc"}, + {file = "rasterio-1.3.9-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:14df8413c030b04e54d478d6ecec4e5958b46585c3cb970bf0dc19b4831146c8"}, + {file = "rasterio-1.3.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:911e54e0bb97c456a045f6d8e24b00aeb055a235d2aa7c2c1f9128f4c6c7a52d"}, + {file = "rasterio-1.3.9-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:01e428ee5ba8444f5cb4fff56225acb1ab9bc8b77209b6e4198e04565d8a8509"}, + {file = "rasterio-1.3.9-cp39-cp39-win_amd64.whl", hash = "sha256:26d9aea05b035927647bb32cc04fad0a68346a2f5186224dc1c2555c33515183"}, + {file = "rasterio-1.3.9.tar.gz", hash = "sha256:fc6d0d290492fa1a5068711cfebb21cc936968891b7ed9da0690c8a7388885c5"}, +] [package.dependencies] affine = "*" attrs = "*" -click = ">=4.0,<8" +certifi = "*" +click = ">=4.0" click-plugins = "*" cligj = ">=0.5" numpy = "*" +setuptools = "*" snuggs = ">=1.4.1" [package.extras] -all = ["boto3 (>=1.2.4)", "sphinx", "pytest-cov (>=2.2.0)", "ghp-import", "packaging", "numpydoc", "ipython (>=2.0)", "sphinx-rtd-theme", "hypothesis", "matplotlib", "pytest (>=2.8.2)", "futures", "mock"] +all = ["boto3 (>=1.2.4)", "ghp-import", "hypothesis", "ipython (>=2.0)", "matplotlib", "numpydoc", "packaging", "pytest (>=2.8.2)", "pytest-cov (>=2.2.0)", "shapely ; python_version < \"3.12\"", "sphinx", "sphinx-rtd-theme"] docs = ["ghp-import", "numpydoc", "sphinx", "sphinx-rtd-theme"] ipython = ["ipython (>=2.0)"] plot = ["matplotlib"] s3 = ["boto3 (>=1.2.4)"] -test = ["pytest (>=2.8.2)", "pytest-cov (>=2.2.0)", "boto3 (>=1.2.4)", "packaging", "hypothesis", "futures", "mock"] +test = ["boto3 (>=1.2.4)", "hypothesis", "packaging", "pytest (>=2.8.2)", "pytest-cov (>=2.2.0)", "shapely ; python_version < \"3.12\""] + +[[package]] +name = "rope" +version = "1.12.0" +description = "a python refactoring library..." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "rope-1.12.0-py3-none-any.whl", hash = "sha256:01f06624159e6b2ec9b94412d519e8877a19eeabe392f469c36f7236df05d443"}, + {file = "rope-1.12.0.tar.gz", hash = "sha256:93a1bb991fbf0426e8d415102d1c092ccc42e826ce9a42c4d61ce53d7786d9d9"}, +] + +[package.dependencies] +pytoolconfig = {version = ">=1.2.2", extras = ["global"]} + +[package.extras] +dev = ["build (>=0.7.0)", "pre-commit (>=2.20.0)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)"] +doc = ["pytoolconfig[doc]", "sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx-rtd-theme (>=1.0.0)"] +release = ["pip-tools (>=6.12.1)", "toml (>=0.10.2)", "twine (>=4.0.2)"] [[package]] name = "rtree" version = "0.8.3" description = "R-Tree spatial index for Python GIS" -category = "main" optional = false python-versions = "*" +groups = ["main"] +files = [ + {file = "Rtree-0.8.3-py2-none-any.whl", hash = "sha256:8526431aa15d8cea1c07b451ce853b62b1da4cf6b74c07810aba5b81d12efe66"}, + {file = "Rtree-0.8.3-py3-none-any.whl", hash = "sha256:a9e67386073f93f22449f396d5993dfe479335169c376f3f7fb04a396391f0dc"}, + {file = "Rtree-0.8.3.tar.gz", hash = "sha256:6cb9cf3000963ea6a3db777a597baee2bc55c4fc891e4f1967f262cc96148649"}, +] + +[package.dependencies] +setuptools = "*" [[package]] -name = "shapely" -version = "1.7.1" -description = "Geometric objects, predicates, and operations" -category = "main" +name = "setuptools" +version = "69.0.3" +description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = "*" +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, +] [package.extras] -all = ["numpy", "pytest", "pytest-cov"] -test = ["pytest", "pytest-cov"] -vectorized = ["numpy"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov ; platform_python_implementation != \"PyPy\"", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-ruff ; sys_platform != \"cygwin\"", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main", "dev"] +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] [[package]] name = "snuggs" version = "1.4.7" description = "Snuggs are s-expressions for Numpy" -category = "main" optional = false python-versions = "*" +groups = ["main"] +files = [ + {file = "snuggs-1.4.7-py3-none-any.whl", hash = "sha256:988dde5d4db88e9d71c99457404773dabcc7a1c45971bfbe81900999942d9f07"}, + {file = "snuggs-1.4.7.tar.gz", hash = "sha256:501cf113fe3892e14e2fee76da5cd0606b7e149c411c271898e6259ebde2617b"}, +] [package.dependencies] numpy = "*" pyparsing = ">=2.1.6" [package.extras] -test = ["pytest", "hypothesis"] +test = ["hypothesis", "pytest"] [[package]] -name = "typing-extensions" -version = "3.7.4.3" -description = "Backported and Experimental Type Hints for Python 3.5+" -category = "dev" +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" +groups = ["dev"] +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] -name = "wcwidth" -version = "0.2.5" -description = "Measures the displayed width of unicode strings in a terminal" -category = "dev" +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" optional = false -python-versions = "*" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] [[package]] -name = "zipp" -version = "3.4.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +[[package]] +name = "traitlets" +version = "5.14.1" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, + {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, +] -[metadata] -lock-version = "1.1" -python-versions = "^3.7" -content-hash = "b692293af7f922a0ec4ca556d4b11b27370173edad323d7a78aa3a0ef73cd790" +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] -[metadata.files] -affine = [ - {file = "affine-2.3.0-py2.py3-none-any.whl", hash = "sha256:34b05b070d954c382e56f02c207a372d8a32621a87653cc30cdd31cd7f65799f"}, - {file = "affine-2.3.0.tar.gz", hash = "sha256:2e045def1aa29e613c42e801a7e10e0b9bacfed1a7c6af0cadf8843530a15102"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] -attrs = [ - {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, - {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, -] -certifi = [ - {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, - {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, -] -click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, -] -click-plugins = [ - {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, - {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, -] -cligj = [ - {file = "cligj-0.7.1-py3-none-any.whl", hash = "sha256:07171c1e287f45511f97df4ea071abc5d19924153413d5683a8e4866369bc676"}, - {file = "cligj-0.7.1.tar.gz", hash = "sha256:b2f1f7247d59a5387bd3013a08b9ed6829e96fafa4a6e6292341efdb46fe6220"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] -fiona = [ - {file = "Fiona-1.8.18-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c64cc0bab040c3a2aec12edd2e1407e3b52609117d9a9a471e3d25172abb1e5e"}, - {file = "Fiona-1.8.18-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:8891d25bcb795eec2294390b56426fded566bcc8ed3730dac7e7a61cde71f1b5"}, - {file = "Fiona-1.8.18-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9fdaaebd5b1b2dd59a0bce9f8ca3e3fba3ef86346be603985590773e4822a543"}, - {file = "Fiona-1.8.18-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8a818fa6cd1ae7ef3b3061ca8f52b694557d2fcf303d7c5431dd961ea09a5ccf"}, - {file = "Fiona-1.8.18-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e34612996121a66559fac9013fd30d8200ed17df54c71edf63461a8b6f519cf5"}, - {file = "Fiona-1.8.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0ec331f9cf43bb3399d41a04f8b7ee5ebddea93de3e780fba98f3bf75b39eb3"}, - {file = "Fiona-1.8.18-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:67df21101e185bbb611846138a3094ee3196b746aa336be3db3a64d7971b8fed"}, - {file = "Fiona-1.8.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5a77bcd113dfa2424ff97190b3dc907640777615490e55777e8042a5b84ed78"}, - {file = "Fiona-1.8.18-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a3de7c1da06bc036dcd8954fa30e089587af335d4e549539169b1de9fe8badba"}, - {file = "Fiona-1.8.18-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1c26f38bfbcd51e710c361f26db605ccf2b5f2a7967e0f4a88683cac3845c947"}, - {file = "Fiona-1.8.18.tar.gz", hash = "sha256:b732ece0ff8886a29c439723a3e1fc382718804bb057519d537a81308854967a"}, -] -importlib-metadata = [ - {file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"}, - {file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"}, -] -jinja2 = [ - {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, - {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, -] -markupsafe = [ - {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, -] -more-itertools = [ - {file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"}, - {file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"}, -] -munch = [ - {file = "munch-2.5.0-py2.py3-none-any.whl", hash = "sha256:6f44af89a2ce4ed04ff8de41f70b226b984db10a91dcc7b9ac2efc1c77022fdd"}, - {file = "munch-2.5.0.tar.gz", hash = "sha256:2d735f6f24d4dba3417fa448cae40c6e896ec1fdab6cdb5e6510999758a4dbd2"}, -] -numpy = [ - {file = "numpy-1.19.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6"}, - {file = "numpy-1.19.4-cp36-cp36m-win32.whl", hash = "sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1"}, - {file = "numpy-1.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb"}, - {file = "numpy-1.19.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387"}, - {file = "numpy-1.19.4-cp37-cp37m-win32.whl", hash = "sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36"}, - {file = "numpy-1.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c"}, - {file = "numpy-1.19.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db"}, - {file = "numpy-1.19.4-cp38-cp38-win32.whl", hash = "sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac"}, - {file = "numpy-1.19.4-cp38-cp38-win_amd64.whl", hash = "sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce"}, - {file = "numpy-1.19.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753"}, - {file = "numpy-1.19.4-cp39-cp39-win32.whl", hash = "sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f"}, - {file = "numpy-1.19.4-cp39-cp39-win_amd64.whl", hash = "sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b"}, - {file = "numpy-1.19.4-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08"}, - {file = "numpy-1.19.4.zip", hash = "sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512"}, -] -packaging = [ - {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, - {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, -] -pandas = [ - {file = "pandas-0.25.3-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:df8864824b1fe488cf778c3650ee59c3a0d8f42e53707de167ba6b4f7d35f133"}, - {file = "pandas-0.25.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7458c48e3d15b8aaa7d575be60e1e4dd70348efcd9376656b72fecd55c59a4c3"}, - {file = "pandas-0.25.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:61741f5aeb252f39c3031d11405305b6d10ce663c53bc3112705d7ad66c013d0"}, - {file = "pandas-0.25.3-cp35-cp35m-win32.whl", hash = "sha256:adc3d3a3f9e59a38d923e90e20c4922fc62d1e5a03d083440468c6d8f3f1ae0a"}, - {file = "pandas-0.25.3-cp35-cp35m-win_amd64.whl", hash = "sha256:975c461accd14e89d71772e89108a050fa824c0b87a67d34cedf245f6681fc17"}, - {file = "pandas-0.25.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ee50c2142cdcf41995655d499a157d0a812fce55c97d9aad13bc1eef837ed36c"}, - {file = "pandas-0.25.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4545467a637e0e1393f7d05d61dace89689ad6d6f66f267f86fff737b702cce9"}, - {file = "pandas-0.25.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bbe3eb765a0b1e578833d243e2814b60c825b7fdbf4cdfe8e8aae8a08ed56ecf"}, - {file = "pandas-0.25.3-cp36-cp36m-win32.whl", hash = "sha256:8153705d6545fd9eb6dd2bc79301bff08825d2e2f716d5dced48daafc2d0b81f"}, - {file = "pandas-0.25.3-cp36-cp36m-win_amd64.whl", hash = "sha256:26382aab9c119735908d94d2c5c08020a4a0a82969b7e5eefb92f902b3b30ad7"}, - {file = "pandas-0.25.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:00dff3a8e337f5ed7ad295d98a31821d3d0fe7792da82d78d7fd79b89c03ea9d"}, - {file = "pandas-0.25.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e45055c30a608076e31a9fcd780a956ed3b1fa20db61561b8d88b79259f526f7"}, - {file = "pandas-0.25.3-cp37-cp37m-win32.whl", hash = "sha256:255920e63850dc512ce356233081098554d641ba99c3767dde9e9f35630f994b"}, - {file = "pandas-0.25.3-cp37-cp37m-win_amd64.whl", hash = "sha256:22361b1597c8c2ffd697aa9bf85423afa9e1fcfa6b1ea821054a244d5f24d75e"}, - {file = "pandas-0.25.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9962957a27bfb70ab64103d0a7b42fa59c642fb4ed4cb75d0227b7bb9228535d"}, - {file = "pandas-0.25.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:78bf638993219311377ce9836b3dc05f627a666d0dbc8cec37c0ff3c9ada673b"}, - {file = "pandas-0.25.3-cp38-cp38-win32.whl", hash = "sha256:6a3ac2c87e4e32a969921d1428525f09462770c349147aa8e9ab95f88c71ec71"}, - {file = "pandas-0.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:33970f4cacdd9a0ddb8f21e151bfb9f178afb7c36eb7c25b9094c02876f385c2"}, - {file = "pandas-0.25.3.tar.gz", hash = "sha256:52da74df8a9c9a103af0a72c9d5fdc8e0183a90884278db7f386b5692a2220a4"}, -] -pillow = [ - {file = "Pillow-5.4.1-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:18e912a6ccddf28defa196bd2021fe33600cbe5da1aa2f2e2c6df15f720b73d1"}, - {file = "Pillow-5.4.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:267f8e4c0a1d7e36e97c6a604f5b03ef58e2b81c1becb4fccecddcb37e063cc7"}, - {file = "Pillow-5.4.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:051de330a06c99d6f84bcf582960487835bcae3fc99365185dc2d4f65a390c0e"}, - {file = "Pillow-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:825aa6d222ce2c2b90d34a0ea31914e141a85edefc07e17342f1d2fdf121c07c"}, - {file = "Pillow-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:5d95cb9f6cced2628f3e4de7e795e98b2659dfcc7176ab4a01a8b48c2c2f488f"}, - {file = "Pillow-5.4.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ba04f57d1715ca5ff74bb7f8a818bf929a204b3b3c2c2826d1e1cc3b1c13398c"}, - {file = "Pillow-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f227d7e574d050ff3996049e086e1f18c7bd2d067ef24131e50a1d3fe5831fbc"}, - {file = "Pillow-5.4.1-cp34-cp34m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:3273a28734175feebbe4d0a4cde04d4ed20f620b9b506d26f44379d3c72304e1"}, - {file = "Pillow-5.4.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:cee815cc62d136e96cf76771b9d3eb58e0777ec18ea50de5cfcede8a7c429aa8"}, - {file = "Pillow-5.4.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:4d4bc2e6bb6861103ea4655d6b6f67af8e5336e7216e20fff3e18ffa95d7a055"}, - {file = "Pillow-5.4.1-cp34-cp34m-win32.whl", hash = "sha256:a6523a23a205be0fe664b6b8747a5c86d55da960d9586db039eec9f5c269c0e6"}, - {file = "Pillow-5.4.1-cp34-cp34m-win_amd64.whl", hash = "sha256:505738076350a337c1740a31646e1de09a164c62c07db3b996abdc0f9d2e50cf"}, - {file = "Pillow-5.4.1-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7eda4c737637af74bac4b23aa82ea6fbb19002552be85f0b89bc27e3a762d239"}, - {file = "Pillow-5.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:163136e09bd1d6c6c6026b0a662976e86c58b932b964f255ff384ecc8c3cefa3"}, - {file = "Pillow-5.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9c215442ff8249d41ff58700e91ef61d74f47dfd431a50253e1a1ca9436b0697"}, - {file = "Pillow-5.4.1-cp35-cp35m-win32.whl", hash = "sha256:0ae5289948c5e0a16574750021bd8be921c27d4e3527800dc9c2c1d2abc81bf7"}, - {file = "Pillow-5.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:801ddaa69659b36abf4694fed5aa9f61d1ecf2daaa6c92541bbbbb775d97b9fe"}, - {file = "Pillow-5.4.1-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:cd878195166723f30865e05d87cbaf9421614501a4bd48792c5ed28f90fd36ca"}, - {file = "Pillow-5.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fc9a12aad714af36cf3ad0275a96a733526571e52710319855628f476dcb144e"}, - {file = "Pillow-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d7c1c06246b05529f9984435fc4fa5a545ea26606e7f450bdbe00c153f5aeaad"}, - {file = "Pillow-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:0b1efce03619cdbf8bcc61cfae81fcda59249a469f31c6735ea59badd4a6f58a"}, - {file = "Pillow-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a631fd36a9823638fe700d9225f9698fb59d049c942d322d4c09544dc2115356"}, - {file = "Pillow-5.4.1-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:24ec3dea52339a610d34401d2d53d0fb3c7fd08e34b20c95d2ad3973193591f1"}, - {file = "Pillow-5.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e9c8066249c040efdda84793a2a669076f92a301ceabe69202446abb4c5c5ef9"}, - {file = "Pillow-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c678e23006798fc8b6f4cef2eaad267d53ff4c1779bd1af8725cc11b72a63f3"}, - {file = "Pillow-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:b117287a5bdc81f1bac891187275ec7e829e961b8032c9e5ff38b70fd036c78f"}, - {file = "Pillow-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d1722b7aa4b40cf93ac3c80d3edd48bf93b9208241d166a14ad8e7a20ee1d4f3"}, - {file = "Pillow-5.4.1-pp260-pypy_41-win32.whl", hash = "sha256:a3d90022f2202bbb14da991f26ca7a30b7e4c62bf0f8bf9825603b22d7e87494"}, - {file = "Pillow-5.4.1-pp360-pp360-win32.whl", hash = "sha256:a756ecf9f4b9b3ed49a680a649af45a8767ad038de39e6c030919c2f443eb000"}, - {file = "Pillow-5.4.1-py2.7-macosx-10.13-x86_64.egg", hash = "sha256:634209852cc06c0c1243cc74f8fdc8f7444d866221de51125f7b696d775ec5ca"}, - {file = "Pillow-5.4.1-py2.7-win-amd64.egg", hash = "sha256:0cf0208500df8d0c3cad6383cd98a2d038b0678fd4f777a8f7e442c5faeee81d"}, - {file = "Pillow-5.4.1-py2.7-win32.egg", hash = "sha256:f71ff657e63a9b24cac254bb8c9bd3c89c7a1b5e00ee4b3997ca1c18100dac28"}, - {file = "Pillow-5.4.1-py3.4-win-amd64.egg", hash = "sha256:01a501be4ae05fd714d269cb9c9f145518e58e73faa3f140ddb67fae0c2607b1"}, - {file = "Pillow-5.4.1-py3.4-win32.egg", hash = "sha256:4baab2d2da57b0d9d544a2ce0f461374dd90ccbcf723fe46689aff906d43a964"}, - {file = "Pillow-5.4.1-py3.5-win-amd64.egg", hash = "sha256:f62b1aeb5c2ced8babd4fbba9c74cbef9de309f5ed106184b12d9778a3971f15"}, - {file = "Pillow-5.4.1-py3.5-win32.egg", hash = "sha256:e9f13711780c981d6eadd6042af40e172548c54b06266a1aabda7de192db0838"}, - {file = "Pillow-5.4.1-py3.6-win-amd64.egg", hash = "sha256:07c35919f983c2c593498edcc126ad3a94154184899297cc9d27a6587672cbaa"}, - {file = "Pillow-5.4.1-py3.6-win32.egg", hash = "sha256:87fe838f9dac0597f05f2605c0700b1926f9390c95df6af45d83141e0c514bd9"}, - {file = "Pillow-5.4.1-py3.7-win-amd64.egg", hash = "sha256:c8939dba1a37960a502b1a030a4465c46dd2c2bca7adf05fa3af6bea594e720e"}, - {file = "Pillow-5.4.1-py3.7-win32.egg", hash = "sha256:5337ac3280312aa065ed0a8ec1e4b6142e9f15c31baed36b5cd964745853243f"}, - {file = "Pillow-5.4.1.tar.gz", hash = "sha256:5233664eadfa342c639b9b9977190d64ad7aca4edc51a966394d7e08e7f38a9f"}, - {file = "Pillow-5.4.1.win-amd64-py2.7.exe", hash = "sha256:e1555d4fda1db8005de72acf2ded1af660febad09b4708430091159e8ae1963e"}, - {file = "Pillow-5.4.1.win-amd64-py3.4.exe", hash = "sha256:52e2e56fc3706d8791761a157115dc8391319720ad60cc32992350fda74b6be2"}, - {file = "Pillow-5.4.1.win-amd64-py3.5.exe", hash = "sha256:ba6ef2bd62671c7fb9cdb3277414e87a5cd38b86721039ada1464f7452ad30b2"}, - {file = "Pillow-5.4.1.win-amd64-py3.6.exe", hash = "sha256:39fbd5d62167197318a0371b2a9c699ce261b6800bb493eadde2ba30d868fe8c"}, - {file = "Pillow-5.4.1.win-amd64-py3.7.exe", hash = "sha256:5ccd97e0f01f42b7e35907272f0f8ad2c3660a482d799a0c564c7d50e83604d4"}, - {file = "Pillow-5.4.1.win32-py2.7.exe", hash = "sha256:db418635ea20528f247203bf131b40636f77c8209a045b89fa3badb89e1fcea0"}, - {file = "Pillow-5.4.1.win32-py3.4.exe", hash = "sha256:ac036b6a6bac7010c58e643d78c234c2f7dc8bb7e591bd8bc3555cf4b1527c28"}, - {file = "Pillow-5.4.1.win32-py3.5.exe", hash = "sha256:f0e3288b92ca5dbb1649bd00e80ef652a72b657dc94989fa9c348253d179054b"}, - {file = "Pillow-5.4.1.win32-py3.6.exe", hash = "sha256:4132c78200372045bb348fcad8d52518c8f5cfc077b1089949381ee4a61f1c6d"}, - {file = "Pillow-5.4.1.win32-py3.7.exe", hash = "sha256:75d1f20bd8072eff92c5f457c266a61619a02d03ece56544195c56d41a1a0522"}, -] -pluggy = [ - {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, - {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, -] -py = [ - {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, - {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, -] -pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, -] -pytest = [ - {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, - {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, +[[package]] +name = "typing-extensions" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.10\"" +files = [ + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] -python-dateutil = [ - {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, - {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, -] -pytz = [ - {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, - {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, -] -rasterio = [ - {file = "rasterio-1.1.8-1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:a65b61ca8fbb8aeeac30b35878e65651882cbca589ca63223ddd5f7e86d9cd11"}, - {file = "rasterio-1.1.8-1-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:b048fc6d633555ec32ce4d16323cbf4f5621c297e711fde146b574f80e78d537"}, - {file = "rasterio-1.1.8-1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8b308caf105d74f317530264d8cb575573612e4c98ebfbd855418335377a2a82"}, - {file = "rasterio-1.1.8-1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:41faa63ed5a2beb2682ed477b5ff317e453a1ada9712101f57b83e2b87a7b8bc"}, - {file = "rasterio-1.1.8-1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:053a5a425c54eda26a4a2b7e9344ff6ec258694bc887a158f7886381961a6194"}, - {file = "rasterio-1.1.8-1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a55be995cb15b1df474c8ae611ae268aef444d31e6b5ff91b770dc89a0cfa199"}, - {file = "rasterio-1.1.8-1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4968ff5dd0cb966d71ee26ee31681a1ef73dcaf145d8579029425174142615c2"}, - {file = "rasterio-1.1.8-1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b4fae8bed8926cc42d6a042e4ee8990265ea0019940e6b5985a622c9135f9c16"}, - {file = "rasterio-1.1.8-1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5a2bed3f6db60bfc9035ef6d644c57f2af0ae415ef9a97bbe93bc18e04f1c594"}, - {file = "rasterio-1.1.8-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:adcfdf9cb8ad70e3afd1d04abc4f15ff543a30e6064bc44326c44b2d8675afc0"}, - {file = "rasterio-1.1.8-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:c90b5f39fc47bcca0523332757ff0cbf61e33d97b019f59420aa89df8c15bb65"}, - {file = "rasterio-1.1.8-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e1f55a6f7d72a12ca5f45d9d5cf82113819235abd48a8d1289aa17e9e487c399"}, - {file = "rasterio-1.1.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:39e7a0215c0fa45743298a0bfae86299cd4d5c94073dcfcc9907810f0ce275ea"}, - {file = "rasterio-1.1.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9525cff3affa2d7c4f6c0f73968d3b303590a5bf8471fc378724fbc2fd14801a"}, - {file = "rasterio-1.1.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63fdae613b9c3af32ef703d394a083907745d1de447f5ec9473c9416406830a9"}, - {file = "rasterio-1.1.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:00caea6d96f00af8d7ecb4ba8a7ebf49c5c6cebe32e25481b891565fc2946393"}, - {file = "rasterio-1.1.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e09286c8e49c60022ac734d6a664ec0dc77a2f193a289ea45437a58c19514264"}, - {file = "rasterio-1.1.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5de33fe2047a3c45d8bb048690ca16140648b76805c849f25ce6894aacaf93dd"}, - {file = "rasterio-1.1.8.tar.gz", hash = "sha256:f7cac7e2ecf65b4b1eb78c994c63bd429b67dc679b0bc0ecfe487d3d5bf88fd5"}, -] -rtree = [ - {file = "Rtree-0.8.3-py2-none-any.whl", hash = "sha256:8526431aa15d8cea1c07b451ce853b62b1da4cf6b74c07810aba5b81d12efe66"}, - {file = "Rtree-0.8.3-py3-none-any.whl", hash = "sha256:a9e67386073f93f22449f396d5993dfe479335169c376f3f7fb04a396391f0dc"}, - {file = "Rtree-0.8.3.tar.gz", hash = "sha256:6cb9cf3000963ea6a3db777a597baee2bc55c4fc891e4f1967f262cc96148649"}, + +[[package]] +name = "ujson" +version = "5.9.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, + {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, + {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, + {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, + {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, + {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, + {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, + {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, + {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, + {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, + {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, + {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, + {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, ] -shapely = [ - {file = "Shapely-1.7.1-1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:46da0ea527da9cf9503e66c18bab6981c5556859e518fe71578b47126e54ca93"}, - {file = "Shapely-1.7.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4c10f317e379cc404f8fc510cd9982d5d3e7ba13a9cfd39aa251d894c6366798"}, - {file = "Shapely-1.7.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:17df66e87d0fe0193910aeaa938c99f0b04f67b430edb8adae01e7be557b141b"}, - {file = "Shapely-1.7.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:da38ed3d65b8091447dc3717e5218cc336d20303b77b0634b261bc5c1aa2bae8"}, - {file = "Shapely-1.7.1-cp35-cp35m-win32.whl", hash = "sha256:8e7659dd994792a0aad8fb80439f59055a21163e236faf2f9823beb63a380e19"}, - {file = "Shapely-1.7.1-cp35-cp35m-win_amd64.whl", hash = "sha256:791477edb422692e7dc351c5ed6530eb0e949a31b45569946619a0d9cd5f53cb"}, - {file = "Shapely-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e3afccf0437edc108eef1e2bb9cc4c7073e7705924eb4cd0bf7715cd1ef0ce1b"}, - {file = "Shapely-1.7.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8f15b6ce67dcc05b61f19c689b60f3fe58550ba994290ff8332f711f5aaa9840"}, - {file = "Shapely-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:60e5b2282619249dbe8dc5266d781cc7d7fb1b27fa49f8241f2167672ad26719"}, - {file = "Shapely-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:de618e67b64a51a0768d26a9963ecd7d338a2cf6e9e7582d2385f88ad005b3d1"}, - {file = "Shapely-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:182716ffb500d114b5d1b75d7fd9d14b7d3414cef3c38c0490534cc9ce20981a"}, - {file = "Shapely-1.7.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4f3c59f6dbf86a9fc293546de492f5e07344e045f9333f3a753f2dda903c45d1"}, - {file = "Shapely-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:6871acba8fbe744efa4f9f34e726d070bfbf9bffb356a8f6d64557846324232b"}, - {file = "Shapely-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:35be1c5d869966569d3dfd4ec31832d7c780e9df760e1fe52131105685941891"}, - {file = "Shapely-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:052eb5b9ba756808a7825e8a8020fb146ec489dd5c919e7d139014775411e688"}, - {file = "Shapely-1.7.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:90a3e2ae0d6d7d50ff2370ba168fbd416a53e7d8448410758c5d6a5920646c1d"}, - {file = "Shapely-1.7.1-cp38-cp38-win32.whl", hash = "sha256:a3774516c8a83abfd1ddffb8b6ec1b0935d7fe6ea0ff5c31a18bfdae567b4eba"}, - {file = "Shapely-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:6593026cd3f5daaea12bcc51ae5c979318070fefee210e7990cb8ac2364e79a1"}, - {file = "Shapely-1.7.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b40cc7bb089ae4aa9ddba1db900b4cd1bce3925d2a4b5837b639e49de054784f"}, - {file = "Shapely-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2df5260d0f2983309776cb41bfa85c464ec07018d88c0ecfca23d40bfadae2f1"}, - {file = "Shapely-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:a5c3a50d823c192f32615a2a6920e8c046b09e07a58eba220407335a9cd2e8ea"}, - {file = "Shapely-1.7.1.tar.gz", hash = "sha256:1641724c1055459a7e2b8bbe47ba25bdc89554582e62aec23cb3f3ca25f9b129"}, -] -six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, -] -snuggs = [ - {file = "snuggs-1.4.7-py3-none-any.whl", hash = "sha256:988dde5d4db88e9d71c99457404773dabcc7a1c45971bfbe81900999942d9f07"}, - {file = "snuggs-1.4.7.tar.gz", hash = "sha256:501cf113fe3892e14e2fee76da5cd0606b7e149c411c271898e6259ebde2617b"}, + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] -typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, + +[[package]] +name = "wrapt" +version = "1.12.1" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, ] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, + +[[package]] +name = "yapf" +version = "0.40.2" +description = "A formatter for Python code" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "yapf-0.40.2-py3-none-any.whl", hash = "sha256:adc8b5dd02c0143108878c499284205adb258aad6db6634e5b869e7ee2bd548b"}, + {file = "yapf-0.40.2.tar.gz", hash = "sha256:4dab8a5ed7134e26d57c1647c7483afb3f136878b579062b786c9ba16b94637b"}, ] -zipp = [ - {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, - {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, + +[package.dependencies] +importlib-metadata = ">=6.6.0" +platformdirs = ">=3.5.1" +tomli = ">=2.0.1" + +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] +markers = {main = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-ruff"] + +[metadata] +lock-version = "2.1" +python-versions = "^3.8" +content-hash = "7d6da9988fc2582ebfed8a651c76f4badbd1c008b259f43f4df90c469240a52f" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 00000000..53b35d37 --- /dev/null +++ b/poetry.toml @@ -0,0 +1,3 @@ +[virtualenvs] +create = true +in-project = true diff --git a/pyproject.toml b/pyproject.toml index 9649e98f..f1cb99d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,27 +1,35 @@ [tool.poetry] name = "pythia" -version = "2.0.0" +version = "2.3.0" description = "An extensible gridded modeling framework for point-based modeling." authors = ["Christopher Villalobos "] license = "BSD-3-Clause" [tool.poetry.dependencies] -python = "^3.7" -fiona = "^1.8" -jinja2 = "^2.10" +python = "^3.8" +Fiona = "^1.9" +jinja2 = "^3.0" numpy = "^1.16" -pandas = "^0.25" -pillow = "^5.4" rasterio = "^1.0" -shapely = "^1.6" rtree = "^0.8" [tool.poetry.dev-dependencies] pytest = "^5.1" +flake8 = "^3.9.2" +neovim = "^0.3.1" +python-lsp-server = {extras = ["all"], version = "^1.2.4"} +pyls-isort = "^0.2.2" +python-lsp-black = "^1.0.0" +pylsp-rope = "^0.1.7" +pyls-flake8 = "^0.4.0" +ipython = "^8.1.0" [tool.poetry.scripts] pythia = "pythia.cli:main" +[tool.poetry.group.dev.dependencies] +black = "^22.8.0" + [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["setuptools", "poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/pythia/__init__.py b/pythia/__init__.py index d4b07020..2c41930a 100644 --- a/pythia/__init__.py +++ b/pythia/__init__.py @@ -1,3 +1,5 @@ +__license__ = "BSD-3-Clause" + import logging from logging import NullHandler diff --git a/pythia/__main__.py b/pythia/__main__.py index 06137fa9..cd84287e 100644 --- a/pythia/__main__.py +++ b/pythia/__main__.py @@ -1,3 +1,5 @@ +__license__ = "BSD-3-Clause" + import sys from pythia.cli import main diff --git a/pythia/analytic_functions.py b/pythia/analytic_functions.py index 44af1e90..f2919b2e 100644 --- a/pythia/analytic_functions.py +++ b/pythia/analytic_functions.py @@ -1,12 +1,16 @@ +__license__ = "BSD-3-Clause" + def generate_funs(config): funcs = [] - for key,funs in config.items(): + for key, funs in config.items(): fun = {} f, *args = funs.split("::") - if (f == "subtract"): - fun['fun'] = subtract - fun['key'] = key - fun['args'] = args + fun["key"] = key + fun["args"] = args + if f == "subtract": + fun["fun"] = subtract + if f == "from_config": + pass funcs.append(fun) return funcs @@ -14,7 +18,7 @@ def generate_funs(config): def subtract(terms): a = _numberify_term(terms[0]) b = _numberify_term(terms[1]) - return a-b + return a - b def _numberify_term(term): @@ -22,3 +26,9 @@ def _numberify_term(term): return float(term) else: return int(term) + + +def from_config(terms): + lat = _numberify_term(terms[0]) + lng = _numberify_term(terms[1]) + return lat + lng diff --git a/pythia/analytics.py b/pythia/analytics.py index f8861b82..17c1ee35 100644 --- a/pythia/analytics.py +++ b/pythia/analytics.py @@ -1,11 +1,17 @@ +__license__ = "BSD-3-Clause" + import csv import logging import os import shutil +from typing import Optional import rasterio +from rasterio.io import DatasetReader import pythia.analytic_functions import pythia.io import pythia.util +import pythia.plugin +from contextlib import _GeneratorContextManager def get_run_basedir(config, run): @@ -29,15 +35,15 @@ def extract_ll(path): # directory. def final_outputs(config, outputs): analytics_config = config.get("analytics_setup", {}) - out_files = [] out_dir = os.path.join(config.get("workDir", ".")) file_prefix = "{}_".format(analytics_config.get("per_pixel_prefix", "pp")) if not os.path.exists(out_dir): os.makedirs(out_dir) for current_file in outputs: - bn = os.path.basename(current_file) - out_file = os.path.join(out_dir, bn[bn.find(file_prefix):]) - shutil.copyfile(current_file, out_file) + bn = os.path.basename(current_file) + out_file = os.path.join(out_dir, bn[bn.find(file_prefix) :]) + shutil.copyfile(current_file, out_file) + def filter_columns(config, outputs): analytics_config = config.get("analytics_setup", {}) @@ -48,17 +54,24 @@ def filter_columns(config, outputs): os.makedirs(out_dir) for current_file in outputs: col_indexes = [] - out_file = os.path.join(out_dir, "filtered_{}".format(os.path.basename(current_file))) - with open(current_file) as source, open(out_file, "w") as dest: + out_file = os.path.join( + out_dir, "filtered_{}".format(os.path.basename(current_file)) + ) + with open(current_file) as source, open(out_file, "w", newline = '') as dest: dssat_in = csv.reader(source) dssat_out = csv.writer(dest) - for line in dssat_in: - if dssat_in.line_num == 1: - for x, col in enumerate(line): - if col in columns: - col_indexes.append(x) - row = [line[idx] for idx in col_indexes] - dssat_out.writerow(row) + try: + for line in dssat_in: + if dssat_in.line_num == 1: + for x, col in enumerate(line): + if col in columns: + col_indexes.append(x) + row = [line[idx] for idx in col_indexes] + dssat_out.writerow(row) + except csv.Error as e: + logging.error( + "CSV error in %s on line %d: %s", current_file, dssat_in.line_num, e + ) out_files.append(out_file) return out_files @@ -66,47 +79,59 @@ def filter_columns(config, outputs): def calculate_columns(config, outputs): analytics_config = config.get("analytics_setup", {}) calculations = analytics_config.get("calculatedColumns", []) - funs = pythia.analytic_functions.generate_funs(calculations) - arg_columns = [] - for fun in funs: - for a in fun["args"]: - if a.startswith("$"): - if a.upper() not in arg_columns: - arg_columns.append(a[1::].upper()) + out_files = [] out_dir = os.path.join(config.get("workDir", "."), "scratch") if not os.path.exists(out_dir): os.makedirs(out_dir) for current_file in outputs: + funs = pythia.analytic_functions.generate_funs(calculations) + arg_columns = [] + for fun in funs: + for a in fun["args"]: + if a.upper() not in arg_columns: + arg_columns.append(a[1::].upper()) + col_indexes = [] - out_file = os.path.join(out_dir, "calculated_{}".format(os.path.basename(current_file))) - with open(current_file) as source, open(out_file, "w") as dest: + out_file = os.path.join( + out_dir, "calculated_{}".format(os.path.basename(current_file)) + ) + with open(current_file) as source, open(out_file, "w", newline = '') as dest: dssat_in = csv.reader(source) dssat_out = csv.writer(dest) num_cols = 0 - for line in dssat_in: - if dssat_in.line_num != 1: - if num_cols == 0: - print("we have a problem") + try: + for line in dssat_in: + if dssat_in.line_num != 1: + if num_cols == 0: + print("we have a problem") + else: + line = line[0:num_cols] + if dssat_in.line_num == 1: + num_cols = len(line) + col_indexes = [line.index(x) for x in arg_columns] + for fun in funs: + line.append(fun["key"]) + dssat_out.writerow(line) else: - line = line[0:num_cols] - if dssat_in.line_num == 1: - num_cols = len(line) - col_indexes = [line.index(x) for x in arg_columns] - for fun in funs: - line.append(fun["key"]) - dssat_out.writerow(line) - else: - for fun in funs: - line.append( - fun["fun"]( - [ - line[col_indexes[arg_columns.index(x[1::].upper())]] - for x in fun["args"] - ] + for fun in funs: + line.append( + fun["fun"]( + [ + line[ + col_indexes[ + arg_columns.index(x[1::].upper()) + ] + ] + for x in fun["args"] + ] + ) ) - ) - dssat_out.writerow(line) + dssat_out.writerow(line) + except csv.Error as e: + logging.error( + "CSV error in %s on line %d: %s", current_file, dssat_in.line_num, e + ) out_files.append(out_file) return out_files @@ -145,6 +170,10 @@ def collate_outputs(config, run): os.makedirs(out_dir) out_file = os.path.join(out_dir, per_pixel_file_name) harea_info = run.get("harvestArea", None) + pop_info = run.get("population", None) + season_info = run.get("season", None) + mgmt_info = run.get("management", None) + late_season_flag = run.get("lateSeason", False) collected_first_line = False for current_dir in _generated_run_files(work_dir, "summary.csv"): lat, lng = extract_ll(current_dir) @@ -152,47 +181,93 @@ def collate_outputs(config, run): mode = "a" else: mode = "w" - with open(os.path.join(current_dir, "summary.csv")) as source, open(out_file, mode) as dest: - # TODO Fix later, this is hacky with little checks in place + with open(os.path.join(current_dir, "summary.csv")) as source, open( + out_file, mode + ) as dest: + additional_headers = "LATITUDE,LONGITUDE,RUN_NAME" + ds_harea: Optional[_GeneratorContextManager[DatasetReader]] = None + ds_pop: Optional[_GeneratorContextManager[DatasetReader]] = None + band_harea = None + band_pop = None + if season_info: + additional_headers = f"{additional_headers},SEASON,LATE_SEASON" + if mgmt_info: + additional_headers = f"{additional_headers},MGMT" if harea_info: + additional_headers = f"{additional_headers},HARVEST_AREA" harea_tiff = harea_info.split("::")[1] - with rasterio.open(harea_tiff) as ds: - band = ds.read(1) - for i, line in enumerate(source): - if i == 0: - if not collected_first_line: - dest.write( - "LATITUDE,LONGITUDE,HARVEST_AREA,RUN_NAME,{}\n".format( - line.strip() - ) - ) - collected_first_line = True + ds_harea = rasterio.open(harea_tiff) + band_harea = ds_harea.read(1) + if pop_info: + additional_headers = f"{additional_headers},POPULATION" + pop_tiff = pop_info.split("::")[1] + ds_pop = rasterio.open(pop_tiff) + band_pop = ds_pop.read(1) + band_pop = ds_pop.read(1) + for i, line in enumerate(source): + if i == 0: + if not collected_first_line: + dest.write("{},{}\n".format(additional_headers, line.strip())) + collected_first_line = True + else: + to_write = (lat, lng, run.get("name", "")) + if season_info is not None: + to_write = to_write + (season_info,) + if late_season_flag: + to_write = to_write + (str(True),) else: - harea = pythia.io.get_site_raster_value( - ds, band, (float(lng), float(lat)) + to_write = to_write + (str(False),) + if mgmt_info is not None: + to_write = to_write + (mgmt_info,) + if ds_harea is not None and not ds_harea.closed: + harea = pythia.io.get_site_raster_value( + ds_harea, band_harea, (float(lng), float(lat)) + ) + if harea is None: + harea = 0 + logging.warning( + "%s, %s is giving an invalid harea, replacing with 0" ) - if harea is None: - harea = 0 - logging.warning("%s, %s is giving an invalid harea, replacing with 0") - harea_s = "{:0.2f}".format(harea) - dest.write( - "{},{},{},{},{}\n".format( - lat, lng, harea_s, run.get("name", ""), line.strip() - ) + harea_s = "{:0.2f}".format(harea) + to_write = to_write + (harea_s,) + if ds_pop is not None and not ds_pop.closed: + pop = pythia.io.get_site_raster_value( + ds_pop, band_pop, (float(lng), float(lat)) + ) + if pop is None: + pop = 0 + logging.warning( + "%s, %s is giving an invalid population, replacing with 0" ) + pop_s = "{:0.2f}".format(pop) + to_write = to_write + (pop_s,) + to_write = to_write + (line.strip() + "\n",) + dest.write(",".join(to_write)) + if ds_harea is not None: + ds_harea.close() + if ds_pop is not None: + ds_pop.close() return out_file def execute(config, plugins): runs = config.get("runs", []) analytics_config = config.get("analytics_setup", None) + + if not analytics_config or len(runs) == 0: + logging.warning("Skipping analytics: no configuration or runs found.") + return + + pythia.plugin.run_plugin_functions( + pythia.plugin.PluginHook.pre_analytics, + plugins, + config=config, + ) + run_outputs = [] calculated = None filtered = None - if not analytics_config: - return - if len(runs) == 0: - return + for run in runs: run_outputs.append(collate_outputs(config, run)) # Apply all the filters first @@ -208,3 +283,12 @@ def execute(config, plugins): combine_outputs(config, filtered) else: final_outputs(config, filtered) + + pythia.plugin.run_plugin_functions( + pythia.plugin.PluginHook.post_analytics, + plugins, + config=config, + run_outputs=run_outputs, + calculated=calculated, + filtered=filtered, + ) diff --git a/pythia/cache_manager.py b/pythia/cache_manager.py index a4d06b03..65db5578 100644 --- a/pythia/cache_manager.py +++ b/pythia/cache_manager.py @@ -1 +1,3 @@ -cache = {} \ No newline at end of file +__license__ = "BSD-3-Clause" + +cache = {} diff --git a/pythia/cli.py b/pythia/cli.py index 538d5b8f..917f7664 100644 --- a/pythia/cli.py +++ b/pythia/cli.py @@ -1,3 +1,5 @@ +__license__ = "BSD-3-Clause" + import argparse import datetime import logging @@ -8,26 +10,44 @@ import pythia.analytics import pythia.io import pythia.peerless +import pythia.rescale import pythia.plugin def main(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(prog="pythia") parser.add_argument("config", help="JSON configuration file to run") - parser.add_argument("--all", action="store_true", help="Run all the steps in pythia") + parser.add_argument( + "--all", action="store_true", help="Run all the steps in pythia" + ) parser.add_argument( "--export-runlist", action="store_true", help="Export a list of all the directories to be run", ) - parser.add_argument("--setup", action="store_true", help="Setup DSSAT run structure and files") - parser.add_argument("--run-dssat", action="store_true", help="Run DSSAT over the run structure") + parser.add_argument( + "--rescale", action="store_true", help="Rescale the input files" + ) + parser.add_argument( + "--setup", action="store_true", help="Setup DSSAT run structure and files" + ) + parser.add_argument( + "--run-dssat", action="store_true", help="Run DSSAT over the run structure" + ) parser.add_argument( "--analyze", action="store_true", help="Run the analysis for the DSSAT runs" ) parser.add_argument( - "--clean-work-dir", action="store_true", help="Clean the work directory prior to run" + "--clean-work-dir", + action="store_true", + help="Clean the work directory prior to run", + ) + parser.add_argument( + "--logfile-prefix", + default="pythia", + help="Prefix the log file with this string. -YYYYmmdd-hhMMSS.log", ) + parser.add_argument("--quiet", action="store_true", help="Enjoy the silence") args = parser.parse_args() if not args.config: @@ -35,12 +55,19 @@ def main(): else: now = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") logging.getLogger("pythia_app") - logging.basicConfig(level=logging.INFO, filename="pythia-{}.log".format(now), filemode="w") + logging.basicConfig( + level=logging.INFO, + filename="{}-{}.log".format(args.logfile_prefix, now), + filemode="w", + ) config = pythia.config.load_config(args.config) if args is None or not config: print("Invalid configuration file") else: - logging.info("Pythia started: %s", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + logging.info( + "Pythia started: %s", + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + ) if args.clean_work_dir: print("Cleaning the work directory") if os.path.exists(config["workDir"]): @@ -52,7 +79,14 @@ def main(): plugins = pythia.plugin.load_plugins(config, {}) config = pythia.plugin.run_plugin_functions( pythia.plugin.PluginHook.post_config, plugins, full_config=config - ) + ).get("full_config", config) + if args.quiet: + config["silence"] = True + else: + config["silence"] = False + if args.all or args.rescale: + print("Rescaling the input files") + config = pythia.rescale.execute(config, plugins) if args.all or args.setup: print("Setting up points and directory structure") pythia.peerless.execute(config, plugins) @@ -62,4 +96,7 @@ def main(): if args.all or args.analyze: print("Running simple analytics over DSSAT directory structure") pythia.analytics.execute(config, plugins) - logging.info("Pythia completed: %s", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + logging.info( + "Pythia completed: %s", + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + ) diff --git a/pythia/config.py b/pythia/config.py index d3c8cbcd..26e53fa6 100644 --- a/pythia/config.py +++ b/pythia/config.py @@ -1,3 +1,5 @@ +__license__ = "BSD-3-Clause" + import itertools import json import logging @@ -13,8 +15,7 @@ def load_config(config_file, validate=True, merge=True): with open(config_file) as f: config = json.load(f) except OSError: - logging.error( - "Error opening configuration file: {}".format(config_file)) + logging.error("Error opening configuration file: {}".format(config_file)) else: if validate: if not _validate_config(config): @@ -27,11 +28,14 @@ def load_config(config_file, validate=True, merge=True): def _check_raster_profile(raster_file, cached_crs): logging.debug("Checking file {}".format(raster_file)) try: - current_crs = pythia.io.get_rio_profile(raster_file)['crs'].data + current_crs = pythia.io.get_rio_profile(raster_file)["crs"].data if cached_crs: if cached_crs != current_crs: - logging.error("CRS mismatch for file {}. {} -> {}. Please reconcile.".format( - raster_file, cached_crs['init'], current_crs['init'])) + logging.error( + "CRS mismatch for file {}. {} -> {}. Please reconcile.".format( + raster_file, cached_crs["init"], current_crs["init"] + ) + ) return cached_crs == current_crs, cached_crs else: return True, current_crs @@ -44,9 +48,17 @@ def _validate_config(config): valid = True # Raster check pass 1 - all files are available and of the same projections values_iter = itertools.chain.from_iterable( - [list(config["default_setup"].values())] + [list(r.values()) for r in config["runs"]]) - rasters = list(set([pythia.functions.extract_raster(raster) for raster in - list(filter(lambda x: "raster::" in str(x), values_iter))])) + [list(config["default_setup"].values())] + + [list(r.values()) for r in config["runs"]] + ) + rasters = list( + set( + [ + pythia.functions.extract_raster(raster) + for raster in list(filter(lambda x: "raster::" in str(x), values_iter)) + ] + ) + ) cached_crs = None for r in rasters: current_return, cached_crs = _check_raster_profile(r, cached_crs) @@ -75,5 +87,7 @@ def _set_run_workdir(run, root, idx): def _merge_runs(config): runs = [_merge_default(config["default_setup"], r) for r in config["runs"]] - runs = [_set_run_workdir(r, config.get("workDir", "."), i) for (i, r) in enumerate(runs)] + runs = [ + _set_run_workdir(r, config.get("workDir", "."), i) for (i, r) in enumerate(runs) + ] return runs diff --git a/pythia/dssat.py b/pythia/dssat.py index 222c1394..173298b4 100644 --- a/pythia/dssat.py +++ b/pythia/dssat.py @@ -1,14 +1,23 @@ +__license__ = "BSD-3-Clause" + import logging import multiprocessing as mp import os import subprocess from multiprocessing.pool import Pool +import pythia.plugin + +async_error = False + -def _run_dssat(details, config): +def _run_dssat(details, config, plugins): logging.debug("Current WD: {}".format(os.getcwd())) - command_string = "cd {} && {} A {}".format( - details["dir"], config["dssat"]["executable"], details["file"] + run_mode = "A" + if "run_mode" in config["dssat"]: + run_mode = config["dssat"]["run_mode"].upper() + command_string = "cd {} && {} {} {}".format( + details["dir"], config["dssat"]["executable"], run_mode, details["file"] ) # print(".", end="", flush=True) dssat = subprocess.Popen( @@ -16,15 +25,52 @@ def _run_dssat(details, config): ) out, err = dssat.communicate() # print("+", end="", flush=True) - return details["dir"], details["file"], out, err, dssat.returncode + + error_count = len(err.decode().split("\n")) - 1 + hook = pythia.plugin.PluginHook.post_run_pixel_success + if error_count > 0: + hook = pythia.plugin.PluginHook.post_run_pixel_failed + + plugin_transform = pythia.plugin.run_plugin_functions( + hook, + plugins, + input={"details": details, "config": config}, + output={"loc": details["dir"], "xfile": details["file"], "out": out, "err": err, "retcode": dssat.returncode} + ).get("output", {}) + + return plugin_transform.get("loc", details["dir"]), plugin_transform.get("xfile", details["file"]), plugin_transform.get("out", out), plugin_transform.get("err", err), plugin_transform.get("retcode", dssat.returncode) def _generate_run_list(config): runlist = [] for root, _, files in os.walk(config.get("workDir", "."), topdown=False): + batch_mode = config["dssat"].get("run_mode", "A") in { + "B", + "E", + "F", + "L", + "N", + "Q", + "S", + "T", + "Y", + } + target = None + if batch_mode: + target = config["dssat"].get("batch_file", None) + else: + target = config["dssat"].get("filex", None) for name in files: - if name.upper().endswith("X"): - runlist.append({"dir": root, "file": name}) + if target is not None: + if name == target: + runlist.append({"dir": root, "file": name}) + else: + if batch_mode: + if name.upper().startswith("DSSBATCH"): + runlist.append({"dir": root, "file": name}) + else: + if name.upper().endswith("X"): + runlist.append({"dir": root, "file": name}) return runlist @@ -39,16 +85,44 @@ def display_async(details): out.decode()[:-1], ) print("X", end="", flush=True) + async_error = True else: print(".", end="", flush=True) +def silent_async(details): + loc, xfile, out, error, retcode = details + error_count = len(out.decode().split("\n")) - 1 + if error_count > 0: + logging.warning( + "Check the DSSAT summary file in %s. %d failures occured\n%s", + loc, + error_count, + out.decode()[:-1], + ) + async_error = True + + def execute(config, plugins): pool_size = config.get("cores", mp.cpu_count()) run_list = _generate_run_list(config) with Pool(processes=pool_size) as pool: for details in run_list: # _generate_run_list(config): - pool.apply_async(_run_dssat, (details, config), callback=display_async) + if config["silence"]: + pool.apply_async(_run_dssat, (details, config, plugins), callback=silent_async) + else: + pool.apply_async(_run_dssat, (details, config, plugins), callback=display_async) pool.close() pool.join() - print("\nIf you see an X above, please check the pythia.log for more details") + + if async_error: + print( + "\nOne or more simulations had failures. Please check the pythia log for more details" + ) + + pythia.plugin.run_plugin_functions( + pythia.plugin.PluginHook.post_run_all, + plugins, + config=config, + run_list=run_list, + ) diff --git a/pythia/functions.py b/pythia/functions.py index 6055f468..1e5d7126 100644 --- a/pythia/functions.py +++ b/pythia/functions.py @@ -1,4 +1,7 @@ +__license__ = "BSD-3-Clause" + import datetime + import logging import os @@ -20,8 +23,8 @@ def xy_from_vector(v): return pythia.io.extract_vector_coords(args[1]) -def xy_from_list(l): - return [tuple(x[::-1]) for x in l] +def xy_from_list(lst): + return [tuple(x[::-1]) for x in lst] def auto_planting_window(k, run, context, _): @@ -40,6 +43,39 @@ def auto_planting_window(k, run, context, _): "plast": pythia.util.to_iso_date(last), } +def auto_planting_window_doy(k, run, context, _): + """multiple rasters not yet supported""" + args = run[k].split("::")[1:] + raster_idx = args.index("raster") + args[raster_idx + 1] = context[k] + args.pop(raster_idx) + vals = [int(v) for v in args] + first = datetime.datetime(run["startYear"], 1, 1) + datetime.timedelta(vals[0] + vals[1] - 1) + td = datetime.timedelta(days=vals[2]) + last = first + td + return { + "pdate": pythia.util.to_iso_date(first), + "pfrst": pythia.util.to_iso_date(first), + "plast": pythia.util.to_iso_date(last), + } + +def auto_planting_window_doy_shape(k, run, context, _): + """multiple rasters not yet supported""" + args = run[k].split("::")[1:] + finder = pythia.io.find_closest_vector_coords + cell_doy = None + if "vector" in args: + idx = args.index("vector") + cell_doy = finder(args[idx + 1], context["lng"], context["lat"], args[idx + 2]) + + first = datetime.datetime(run["startYear"], 1, 1) + datetime.timedelta(int(cell_doy) + int(args[idx + 3]) ) + td = datetime.timedelta(days=int(args[idx + 4])) + last = first + td + return { + "pdate": pythia.util.to_iso_date(first), + "pfrst": pythia.util.to_iso_date(first), + "plast": pythia.util.to_iso_date(last), + } def lookup_hc27(k, run, context, _): args = run[k].split("::")[1:] @@ -52,12 +88,11 @@ def lookup_hc27(k, run, context, _): def lookup_wth(k, run, context, _): args = run[k].split("::")[1:] finder = pythia.io.find_closest_vector_coords + cell_id = None if "vector" in args: idx = args.index("vector") - cell_id = finder( - args[idx + 1], context["lng"], context["lat"], args[idx + 2] - ) - return {k: args[0], "wthFile": "{}.WTH".format(cell_id)} + cell_id = finder(args[idx + 1], context["lng"], context["lat"], args[idx + 2]) + return {k: args[0], "wthFile": "{}.WTH".format(int(cell_id))} def generate_ic_layers(k, run, context, _): @@ -66,7 +101,9 @@ def generate_ic_layers(k, run, context, _): profile = args[0][1:] else: profile = args[0] - soil_file = pythia.soil_handler.findSoilProfile(context[profile], context["soilFiles"]) + soil_file = pythia.soil_handler.findSoilProfile( + context[profile], context["soilFiles"] + ) layers = pythia.soil_handler.readSoilLayers(context[profile], soil_file) calculated_layers = pythia.soil_handler.calculateICLayerData(layers, run) layer_labels = ["icbl", "sh2o", "snh4", "sno3"] @@ -75,45 +112,65 @@ def generate_ic_layers(k, run, context, _): def build_ghr_cache(config): import sqlite3 + with sqlite3.connect(os.path.join(config["ghr_root"], "GHR.db")) as conn: - cache["ghr_profiles"] = {} + ghr_profiles = {} conn.row_factory = sqlite3.Row cursor = conn.cursor() - cursor.execute("SELECT * FROM profile_map") + cursor.execute("SELECT * FROM profile_map where profile != ''") for row in cursor.fetchall(): - if row["profile"] == "": - profile = None - else: - profile = row["profile"] - cache["ghr_profiles"][row["id"]] = profile + ghr_profiles[row["id"]] = row["profile"] + + cache["ghr_profiles"] = ghr_profiles; pass + def lookup_ghr(k, run, context, config): args = run[k].split("::")[1:] if "raster" in args: logging.debug("lookup_ghr - context[%s] => %s", k, context[k]) - if not "ghr_profiles" in cache: + if "ghr_profiles" not in cache: build_ghr_cache(config) - tif_profile_id = int(str(context[k])) - id_soil = cache["ghr_profiles"][tif_profile_id] + tif_profile_id = int(float(str(context[k]))) + if tif_profile_id not in cache["ghr_profiles"]: + logging.error( + "Invalid soil ID (%d) at (%f,%f)", + tif_profile_id, + context["lng"], + context["lat"], + ) + return None + id_soil = cache["ghr_profiles"][tif_profile_id] if id_soil and id_soil.strip() != "": sol_file = "{}.SOL".format(id_soil[:2].upper()) - return {k: id_soil, "soilFiles": [os.path.join(config["ghr_root"], sol_file)]} + return { + k: id_soil, + "soilFiles": [os.path.join(config["ghr_root"], sol_file)], + } else: - logging.error("Soil NOT found for id: %s at (%f,%f)", tif_profile_id, context["lng"], context["lat"]) + logging.error( + "Soil NOT found for id: %s at (%f,%f)", + tif_profile_id, + context["lng"], + context["lat"], + ) return None + def split_fert_dap_percent(k, run, context, _): args = run[k].split("::")[1:] if args[0].startswith("$"): - total = run[args[0][1:]] + search_context = args[0][1:] + total = float(context[search_context]) else: total = float(args[0]) # splits = int(args[1]) split_amounts = args[2:] if any(n.startswith("-") for n in split_amounts): - logging.error("No arguments for split_applications_dap_percent should be negative") + logging.error( + "No arguments for split_applications_dap_percent should be negative" + ) return None daps = [int(i) for i in split_amounts[0::2]] percents = [float(i) / 100.0 for i in split_amounts[1::2]] @@ -121,7 +178,9 @@ def split_fert_dap_percent(k, run, context, _): logging.error("Not enough arguments for split_applications_dap_percent") return None if sum(percents) != 1.0: - logging.error("The sum of all percents needs to be 100 in split_applications_dap_percent") + logging.error( + "The sum of all percents needs to be 100 in split_applications_dap_percent" + ) logging.error(percents) return None if len(daps) != len(set(daps)): @@ -135,17 +194,45 @@ def split_fert_dap_percent(k, run, context, _): return {k: out} +def split_fert_dap_qty(k, run, context, _): + args = run[k].split("::")[1:] + split_amounts = args + for i,split_amount in enumerate(split_amounts): + if split_amount.startswith("$"): + search_context = split_amount[1:] + value = float(context[search_context]) + else: + value = float(split_amount) + split_amounts[i]=value + daps = [int(i) for i in split_amounts[0::2]] + qty = [int(i) for i in split_amounts[1::2]] + if len(daps) != len(qty): + logging.error("Not enough arguments for split_applications_dap_qty") + return None + if len(daps) != len(set(daps)): + logging.error("Days should not be the same in split_applications_dap_percent") + return None + out = [] + for i in range(len(daps)): + app_total = qty[i] + app_dap = daps[i] + out.append({"fdap": app_dap, "famn": app_total}) + return {k: out} + + def assign_by_raster_value(k, run, context, _): init_args = run[k].split("::")[1:] if "raster" in init_args: - args = init_args[init_args.index("raster")+2:] + args = init_args[init_args.index("raster") + 2 :] else: logging.error("Need to specify a raster for %s:assign_by_value", k) return None raster_val = [int(i) for i in args[0::2]] assignment = args[1::2] if len(raster_val) != len(assignment): - logging.error("The values and assignments don't pair up in %s:assign_by_raster_value", k) + logging.error( + "The values and assignments don't pair up in %s:assign_by_raster_value", k + ) return None if context[k] in raster_val: rv_idx = raster_val.index(context[k]) @@ -153,3 +240,54 @@ def assign_by_raster_value(k, run, context, _): else: logging.error("No assignment for value %d in %s:assign_by_value", context[k], k) return None + + +def date_from_doy_raster(k, run, context, _): + init_args = run[k].split("::")[1:] + if "raster" not in init_args: + logging.error("date_from_doy_raster: No raster specified.") + return None + if context[k] < 1 or context[k] > 366: + logging.error( + "date_from_doy_raster: Invalid day of year found in raster: %d", context[k] + ) + return None + return { + k: pythia.util.to_iso_date( + pythia.util.from_julian_date(f'{run["startYear"]}{context[k]}') + ) + } + + +def date_offset(k, run, context, _): + args = run[k].split("::")[1:] + offset_value = args[-1] + try: + offset_value = int(offset_value) + except ValueError: + logging.error("date_offset: %s is not an integer", offset_value) + return None + if args[0].startswith("$"): + search_context = args[0][1:] + if search_context not in context: + logging.error("date_offset: %s is not in the current context.", args[0]) + return None + context_date = context[search_context] + cxt_date = pythia.util.from_iso_date(context_date) + td = datetime.timedelta(days=offset_value) + new_date = cxt_date + td + return {k: pythia.util.to_iso_date(new_date)} + else: + logging.error("date_offset only works with references variables.") + return None + + +def string_to_number(term): + try: + if "." in term: + return float(term) + else: + return int(term) + except ValueError: + logging.error("string_to_number: %s is not a number", term) + return None diff --git a/pythia/gis.py b/pythia/gis.py new file mode 100644 index 00000000..39a9d0b1 --- /dev/null +++ b/pythia/gis.py @@ -0,0 +1,19 @@ +def euclidean_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float: + """ + Calculates the euclidean distance between two lat/lon points. + + Designed for fast comparisons (e.g., finding the closest point in a list). + Smaller values indicate closer proximity, but the output is NOT in any standard + unit (km, miles, etc.) and should not be used for actual distance calculations. + + Args: + lat1, lon1: Latitude/longitude of the first point (decimal degrees). + lat2, lon2: Latitude/longitude of the second point (decimal degrees). + + Returns: + A non-negative float representing relative 'distance' (dimensionless). + Only meaningful for comparing proximity between points. + """ + if lat1 == lat2 and lon1 == lon2: + return 0.0 + return (lat1 - lat2)**2 + (lon1 - lon2)**2 diff --git a/pythia/io.py b/pythia/io.py index 4af2658f..32de0944 100644 --- a/pythia/io.py +++ b/pythia/io.py @@ -1,16 +1,20 @@ +__license__ = "BSD-3-Clause" + import os +import sys import fiona import numpy.ma as ma import rasterio -from shapely.geometry import Point, MultiPoint -from shapely.ops import nearest_points +from functools import cache +from typing import Any, Dict, Tuple +from pythia.gis import euclidean_distance import pythia.functions import pythia.util -"""rasterio reads x/y which is longitude/latitude""" +"""rasterio reads x/y which is longitude/latitude""" def get_site_raster_value(dataset, band, site): lng, lat = site @@ -18,7 +22,7 @@ def get_site_raster_value(dataset, band, site): data = [] try: data = band[row, col] - if (data is ma.masked): + if data is ma.masked: data = None except IndexError: data = None @@ -76,9 +80,30 @@ def get_shp_profile(f): pass +@cache +def index_points_ids(file: str, id_field: str) -> Dict[Tuple[float, float], Any]: + """ + Create a mapping of (lon, lat) coordinates to feature IDs from a GIS file. + + :param file: Path to the GIS file (e.g., Shapefile, GeoJSON). + :param id_field: Property field name whose values are mapped to coordinates. + :returns: A dictionary with (longitude, latitude) keys and `id_field` values. + """ + coords_map = {} + with fiona.open(file, "r") as source: + for feature in source: + if feature["geometry"]["type"] == "MultiPoint": + for coords in feature["geometry"]["coordinates"]: + coords_map[(coords[0], coords[1])] = feature["properties"][id_field] + if feature["geometry"]["type"] == "Point": + coords = feature["geometry"]["coordinates"] + coords_map[(coords[0], coords[1])] = feature["properties"][id_field] + return coords_map + + def extract_vector_coords(f): points = [] - with fiona.open(f, 'r') as source: + with fiona.open(f, "r") as source: for feature in source: if feature["geometry"]["type"] == "MultiPoint": points.append(feature["geometry"]["coordinates"][0]) @@ -89,7 +114,7 @@ def extract_vector_coords(f): def find_vector_coords(f, lng, lat, a): coords = (lng, lat) - with fiona.open(f, 'r') as source: + with fiona.open(f, "r") as source: for feature in source: if feature["geometry"]["type"] == "MultiPoint": if coords in feature["geometry"]["coordinates"]: @@ -100,20 +125,28 @@ def find_vector_coords(f, lng, lat, a): def find_closest_vector_coords(f, lng, lat, a): - coords = Point(lng, lat) - points = [] - ids = [] - with fiona.open(f, 'r') as source: + lookup = index_points_ids(f, a)[(lng, lat)] + if lookup is not None: + return lookup + + closest_id = None + with fiona.open(f, "r") as source: + closest_distance = sys.float_info.max for feature in source: if feature["geometry"]["type"] == "MultiPoint": - points.extend([Point(p[0], p[1]) for p in feature["geometry"]["coordinates"]]) - ids.extend([feature["properties"][a]] * len(feature["geometry"]["coordinates"])) + for coords in feature["geometry"]["coordinates"]: + res = euclidean_distance(coords[1], coords[0], lat, lng) + if res < closest_distance: + closest_distance = res + closest_id = feature["properties"][a] + if res == 0: + break if feature["geometry"]["type"] == "Point": - points.append( - Point(feature["geometry"]["coordinates"][0], - feature["geometry"]["coordinates"][1]) - ) - ids.append(feature["properties"][a]) - mp = MultiPoint(points) - nearest = nearest_points(coords, mp)[1] - return ids[points.index(nearest)] + coords = feature["geometry"]["coordinates"] + res = euclidean_distance(coords[1], coords[0], lat, lng) + if res < closest_distance: + closest_distance = res + closest_id = feature["properties"][a] + if res == 0: + return feature["properties"][a] + return closest_id diff --git a/pythia/peerless.py b/pythia/peerless.py index 7a05d696..84e5bf71 100644 --- a/pythia/peerless.py +++ b/pythia/peerless.py @@ -1,17 +1,20 @@ +__license__ = "BSD-3-Clause" + import logging import multiprocessing as mp +import concurrent.futures import os import pythia.functions import pythia.io -import pythia.template import pythia.plugin +import pythia.template import pythia.util -def build_context(args): - print("+", end="", flush=True) - run, ctx, config = args +def build_context(run, ctx, config, plugins): + if not config["silence"]: + print("+", end="", flush=True) context = run.copy() context = {**context, **ctx} y, x = pythia.util.translate_coords_news(context["lat"], context["lng"]) @@ -26,13 +29,25 @@ def build_context(args): else: context = None break + + hook = pythia.plugin.PluginHook.post_peerless_pixel_success + if context is None: + hook = pythia.plugin.PluginHook.post_peerless_pixel_skip + + context = pythia.plugin.run_plugin_functions( + hook, + plugins, + context=context, + args={"run": run, "config": config, "ctx": ctx}, + ).get("context", context) + return context -def _generate_context_args(runs, peers, config): +def _generate_context_args(runs, peers, config, plugins): for idx, run in enumerate(runs): for peer in peers[idx]: - yield run, peer, config + yield run, peer, config, plugins def symlink_wth_soil(output_dir, config, context): @@ -52,11 +67,14 @@ def symlink_wth_soil(output_dir, config, context): for soil in context["soilFiles"]: soil_file = os.path.join(output_dir, os.path.basename(soil)) if not os.path.exists(soil_file): - os.symlink(os.path.abspath(soil), os.path.join(output_dir, os.path.basename(soil))) + os.symlink( + os.path.abspath(soil), os.path.join(output_dir, os.path.basename(soil)) + ) def compose_peerless(context, config, env): - print(".", end="", flush=True) + if not config["silence"]: + print(".", end="", flush=True) this_output_dir = context["contextWorkDir"] symlink_wth_soil(this_output_dir, config, context) xfile = pythia.template.render_template(env, context["template"], context) @@ -65,6 +83,38 @@ def compose_peerless(context, config, env): return context["contextWorkDir"] +def process_context(context, plugins, config, env): + if context is not None: + pythia.io.make_run_directory(context["contextWorkDir"]) + # Post context hook + logging.debug("[PEERLESS] Running post_build_context plugins") + context = pythia.plugin.run_plugin_functions( + pythia.plugin.PluginHook.post_build_context, + plugins, + context=context, + ).get("context", context) + compose_peerless_result = compose_peerless(context, config, env) + compose_peerless_result = pythia.plugin.run_plugin_functions( + pythia.plugin.PluginHook.post_compose_peerless_pixel_success, + plugins, + context=context, + compose_peerless_result=compose_peerless_result, + config=config, + env=env, + ).get("compose_peerless_result", compose_peerless_result) + return os.path.abspath(compose_peerless_result) + else: + pythia.plugin.run_plugin_functions( + pythia.plugin.PluginHook.post_compose_peerless_pixel_skip, + plugins, + context=context, + config=config, + env=env, + ) + if not config["silence"]: + print("X", end="", flush=True) + + def execute(config, plugins): runs = config.get("runs", []) if len(runs) == 0: @@ -72,25 +122,35 @@ def execute(config, plugins): runlist = [] for run in runs: pythia.io.make_run_directory(os.path.join(config["workDir"], run["name"])) + peers = [pythia.io.peer(r, config.get("sample", None)) for r in runs] - pool_size = config.get("threads", mp.cpu_count() * 10) + pool_size = config.get("threads", mp.cpu_count()) print("RUNNING WITH POOL SIZE: {}".format(pool_size)) env = pythia.template.init_engine(config["templateDir"]) - with mp.pool.ThreadPool(pool_size) as pool: - for context in pool.imap_unordered( - build_context, _generate_context_args(runs, peers, config), 250 - ): - if context is not None: - pythia.io.make_run_directory(context["contextWorkDir"]) - # Post context hook - logging.debug("[PEERLESS] Running post_build_context plugins") - context = pythia.plugin.run_plugin_functions( - pythia.plugin.PluginHook.post_build_context, plugins, context=context - ) - runlist.append(os.path.abspath(compose_peerless(context, config, env))) - else: - print("X", end="", flush=True) + pythia.functions.build_ghr_cache(config) + + # Parallelize the context build (build_context), it is CPU intensive because it + # runs the functions (functions.py) declared in the config files. + with concurrent.futures.ProcessPoolExecutor(max_workers=pool_size) as executor: + tasks = _generate_context_args(runs, peers, config, plugins) + future_to_context = {executor.submit(build_context, *task): task for task in tasks} + + # process_context is mostly I/O intensive, no reason to parallelize it. + for future in concurrent.futures.as_completed(future_to_context): + context_result = future.result() + if context_result is not None: + processed_result = process_context(context_result, plugins, config, env) + if processed_result is not None: + runlist.append(processed_result) + if config["exportRunlist"]: with open(os.path.join(config["workDir"], "run_list.txt"), "w") as f: [f.write(f"{x}\n") for x in runlist] - print() + + pythia.plugin.run_plugin_functions( + pythia.plugin.PluginHook.post_compose_peerless_all, + plugins, + run_list=runlist, + config=config, + env=env, + ) diff --git a/pythia/plugin.py b/pythia/plugin.py index 16fa0274..b682d4d4 100644 --- a/pythia/plugin.py +++ b/pythia/plugin.py @@ -1,3 +1,5 @@ +__license__ = "BSD-3-Clause" + import logging from enum import Enum, unique @@ -7,31 +9,42 @@ class PluginHook(Enum): post_config = 100 pre_build_context = 200 post_build_context = 300 + post_peerless_pixel_success = 350 + post_peerless_pixel_skip = 351 + post_compose_peerless_pixel_success = 352 + post_compose_peerless_pixel_skip = 353 + post_compose_peerless_all = 354 post_setup = 400 pre_run = 500 run_pixel = 600 - post_run = 700 - pre_analysis = 800 - analyze_file = 900 - analyze_pixel = 1000 - post_analysis = 1100 + post_run_pixel_success = 650 + post_run_pixel_failed = 651 + post_run_all = 700 + pre_analytics = 800 + post_analytics = 900 def register_plugin_function(hook, fun, config, plugins): # Check to see if the hook is a PluginHook if not isinstance(hook, PluginHook): - logging.warning("[PLUGIN] Ignoring {} because {} is not a PluginHook".format(fun, hook)) + logging.warning( + "[PLUGIN] Ignoring {} because {} is not a PluginHook".format(fun, hook) + ) return plugins # Check to see if the function is a function. if not callable(fun): - logging.warning("[PLUGIN] Ignoring {} because {} is not a function.".format(fun, fun)) + logging.warning( + "[PLUGIN] Ignoring {} because {} is not a function.".format(fun, fun) + ) return plugins # Check to see if the config is an object. if not isinstance(config, dict): logging.warning( - "[PLUGIN] Ignoring {} because {} is not a valid configuration".format(fun, config) + "[PLUGIN] Ignoring {} because {} is not a valid configuration".format( + fun, config + ) ) return plugins @@ -76,25 +89,25 @@ def load_plugins(config, plugins={}, module_prefix="pythia.plugins"): if spec is None: logging.warning("[PLUGIN] Cannot find plugin: {}".format(plugin["plugin"])) continue - _loaded = importlib.import_module("{}.{}".format(module_prefix, plugin["plugin"])) + _loaded = importlib.import_module( + "{}.{}".format(module_prefix, plugin["plugin"]) + ) # TODO: Check to see if the config works for the plugin # TODO: Check to see if the plugin conforms to the correct signature (optional) # Call plugin initialization - _imported = _loaded.initialize(plugin.get("params", {}), _imported, config) + _imported = _loaded.initialize(plugin, _imported, config) return _imported def run_plugin_functions(hook, plugins, **kwargs): - _return = {} - if hook == PluginHook.post_config: - _return = {**kwargs.get("full_config", {})} - elif hook == PluginHook.post_build_context: - _return = {**kwargs.get("context", {})} + _return = {**kwargs} if hook in plugins: for plugin_fun in plugins[hook]: - if hook == PluginHook.post_config: - _return = {**_return, **plugin_fun["fun"](plugin_fun.get("config", {}), _return)} - elif hook == PluginHook.post_build_context: - _return = {**_return, **plugin_fun["fun"](plugin_fun.get("config", {}), _return)} + plugin_fun_return = plugin_fun["fun"](plugin_fun.get("config", {}), _return, **kwargs) + _return = { + **_return, + **({} if plugin_fun_return is None else plugin_fun_return) + } + return _return diff --git a/pythia/plugins/sensitivity_plugin/__init__.py b/pythia/plugins/sensitivity_plugin/__init__.py index 43b4e979..41e00cbb 100644 --- a/pythia/plugins/sensitivity_plugin/__init__.py +++ b/pythia/plugins/sensitivity_plugin/__init__.py @@ -1,6 +1,7 @@ import datetime import itertools import logging +import os from pythia.plugin import register_plugin_function, PluginHook import pythia.util @@ -20,6 +21,7 @@ "values" : ["M0.25", "M1.0", "M1.25"] } }, + "no_rename": true, (optional, defaults to false) "order": 1 ] """ @@ -37,40 +39,76 @@ def initialize(config, plugins, full_config): logging.info("[Sensitivity Plugin] Initializing plugin") - plugins = register_plugin_function(PluginHook.post_config, generate_sensitivity_runs, config, - plugins) - plugins = register_plugin_function(PluginHook.post_build_context, post_build_context_apply_factors, config, plugins) - plugins = register_plugin_function(PluginHook.post_build_context, - post_build_context_apply_static_factors, config, plugins) + cfg = config["params"] + cfg["no_rename"] = config.get("no_rename", False) + plugins = register_plugin_function( + PluginHook.post_config, generate_sensitivity_runs, cfg, plugins + ) + plugins = register_plugin_function( + PluginHook.post_build_context, post_build_context_apply_factors, cfg, plugins + ) + plugins = register_plugin_function( + PluginHook.post_build_context, + post_build_context_apply_static_factors, + cfg, + plugins, + ) return plugins def _assign_static(sens, hook, key, plugin_config): if key not in sens[hook]: sens[hook][key] = [] - sens[hook][key].append({"var": key, "method": plugin_config[key]["method"], - "val": plugin_config[key]["value"], "hook": hook, - "from": plugin_config[key].get("from", key)}) + sens[hook][key].append( + { + "var": key, + "method": plugin_config[key]["method"], + "val": plugin_config[key]["value"], + "hook": hook, + "from": plugin_config[key].get("from", key), + } + ) def _assign_factorial(sens, hook, key, plugin_config): if key not in sens[hook]: sens[hook][key] = [] for v in plugin_config[key]["values"]: - sens[hook][key].append({"var": key, "method": plugin_config[key]["method"], "val": v, "hook": hook}) + sens[hook][key].append( + {"var": key, "method": plugin_config[key]["method"], "val": v, "hook": hook} + ) def merge_static(statics, factor): return list(factor) + statics +def _uniq_factors(factor_list): + uniq = [] + for factors in factor_list: + if len(uniq) == 0: + uniq.append(factors) + else: + uniq_test = [u == factors for u in uniq] + if True in uniq_test: + pass + else: + uniq.append(factors) + return uniq + + def generate_sensitivity_runs(plugin_config={}, full_config={}): - sens = {"_sens_pre_context": {}, - "_sens_pre_context_static": {}, - "_sens_post_context": {}, - "_sens_post_context_static": {}} + sens = { + "_sens_pre_context": {}, + "_sens_pre_context_static": {}, + "_sens_post_context": {}, + "_sens_post_context_static": {}, + } # First we organize them + runs = full_config.get("runs", {}) for k in plugin_config.keys(): + if k == "no_rename": + continue hook = plugin_config[k].get("hook", "post_config").casefold() static = plugin_config[k].get("static", False) context_string = None @@ -79,25 +117,37 @@ def generate_sensitivity_runs(plugin_config={}, full_config={}): elif hook == "post_build_context": context_string = "_sens_post_context" if static: - _assign_static(sens, context_string+"_static", k, plugin_config) + _assign_static(sens, context_string + "_static", k, plugin_config) else: _assign_factorial(sens, context_string, k, plugin_config) - factorial = list(itertools.product(*[*sens["_sens_pre_context"].values(), *sens["_sens_post_context"].values()])) - statics = list(itertools.chain.from_iterable( - [*sens["_sens_pre_context_static"].values(), *sens["_sens_post_context_static"].values()])) - factors = [merge_static(statics, f) for f in factorial] + factorial = list( + itertools.product( + *[*sens["_sens_pre_context"].values(), *sens["_sens_post_context"].values()] + ) + ) + statics = list( + itertools.chain.from_iterable( + [ + *sens["_sens_pre_context_static"].values(), + *sens["_sens_post_context_static"].values(), + ] + ) + ) + combined_factors = [merge_static(statics, f) for f in factorial] # Next we generate the new runs for each analysis - runs = full_config.get("runs", []) out_runs = [] for run in runs: current_name = run["name"] current_workDir = run["workDir"] - for factor in factors: - f = generate_factorial_name(factor) - run["name"] = current_name + "__" + f - run["workDir"] = current_workDir + "__" + f - out_runs.append({**run, **{"_sens": factor}}) + prefilter = [filter_unfactorable(run, f) for f in combined_factors] + uniq = _uniq_factors(prefilter) + for factors in uniq: + if not plugin_config.get("no_rename", False): + f = generate_factorial_name(factors) + run["name"] = current_name + "__" + f + run["workDir"] = current_workDir + "__" + f + out_runs.append({**run, **{"_sens": factors}}) out_runs = [apply_factors("_sens_pre_context", run) for run in out_runs] out_runs = [apply_factors("_sens_pre_context_static", run) for run in out_runs] full_config["runs"] = out_runs @@ -105,7 +155,34 @@ def generate_sensitivity_runs(plugin_config={}, full_config={}): def generate_factorial_name(factors): - return "__".join([factor["var"]+str(factor["val"]) for factor in factors if factor["hook"].endswith("context")]) + return "__".join( + [ + factor["var"] + str(factor["val"]) + for factor in factors + if factor["hook"].endswith("context") + ] + ) + + +def _factorable(run, factor): + if factor["hook"].endswith("static"): + var = factor["from"] + else: + var = factor["var"] + if factor["method"] != "env_mod" and var not in run: + logging.error( + "sensitivity_plugin: %s requires %s to be specified in the JSON config file. This factor is NOT being applied to run %s", + factor["method"], + var, + run["name"], + ) + return False + else: + return True + + +def filter_unfactorable(run, factors): + return [f for f in factors if _factorable(run, f)] def post_build_context_apply_factors(config={}, context={}): @@ -119,18 +196,24 @@ def post_build_context_apply_static_factors(config={}, context={}): def apply_factors(hook, current_context): if current_context is None: return - call_list = {"date_offset": date_offset, - "offset": offset, - "env_mod": env_mod} + call_list = {"date_offset": date_offset, "offset": offset, "env_mod": env_mod} current_factors = [f for f in current_context["_sens"] if f["hook"] == hook] for cf in current_factors: cval = None if hook.endswith("static"): - cval = current_context[cf["from"]] + cvar = cf["from"] + else: + cvar = cf["var"] + cval = current_context.get(cvar, -99) + if cval == -99 and cf["method"] != "env_mod": + logging.error( + "sensitivity_plugin: %s requires %s to be specified in the JSON config file. This factor is NOT being applied.", + cf["method"], + cvar, + ) else: - cval = current_context[cf["var"]] - if cf["method"] in call_list: - current_context[cf["var"]] = call_list[cf["method"]](cval, cf["val"]) + if cf["method"] in call_list: + current_context[cf["var"]] = call_list[cf["method"]](cval, cf["val"]) return current_context @@ -148,7 +231,7 @@ def date_offset(d, offset): def offset(v, offset): - return v+offset + return v + offset def env_mod(v, value): diff --git a/pythia/plugins/test_plugin/__init__.py b/pythia/plugins/test_plugin/__init__.py index 56c44b82..c5c21508 100644 --- a/pythia/plugins/test_plugin/__init__.py +++ b/pythia/plugins/test_plugin/__init__.py @@ -5,19 +5,41 @@ def initialize(config, plugins, full_config): logging.info("[TEST PLUGIN] Initializing plugin") plugins = register_plugin_function(PluginHook.post_config, sample_function, config, plugins) - plugins = register_plugin_function( - PluginHook.post_build_context, contexted_function, config, plugins - ) + plugins = register_plugin_function(PluginHook.post_build_context, contexted_function, config, plugins) + plugins = register_plugin_function(PluginHook.post_peerless_pixel_success, on_peerless_success, config, plugins) + plugins = register_plugin_function(PluginHook.post_peerless_pixel_skip, on_peerless_skip, config, plugins) + plugins = register_plugin_function(PluginHook.post_run_pixel_success, on_run_pixel_success, config, plugins) + plugins = register_plugin_function(PluginHook.post_run_pixel_failed, on_run_pixel_failed, config, plugins) return plugins -def sample_function(config={}): +def sample_function(config={}, **kwargs): retval = config.get("value", 1) logging.info("[TEST PLUGIN] Running the sample_function()") - return retval + return {**kwargs, "config": config, "retval": retval} -def contexted_function(config={}, context={}): +def contexted_function(context={}, **kwargs): logging.info("[TEST PLUGIN] Running the contexted_function()") context["context_value"] = context.get("context_value", 2) + 1 - return context + return {**kwargs, "context": context} + + +def on_peerless_success(*args, **kwargs): + logging.info("[TEST PLUGIN] peerless success") + return kwargs + + +def on_peerless_skip(*args, **kwargs): + logging.info("[TEST PLUGIN] peerless skip") + return kwargs + + +def on_run_pixel_success(*args, **kwargs): + logging.info("[TEST PLUGIN] run pixel success") + return kwargs + + +def on_run_pixel_failed(*args, **kwargs): + logging.info("[TEST PLUGIN] run pixel failed") + return kwargs diff --git a/pythia/plugins/weather_forecast_simple/__init__.py b/pythia/plugins/weather_forecast_simple/__init__.py index 5dc6f03a..cf9ab88e 100644 --- a/pythia/plugins/weather_forecast_simple/__init__.py +++ b/pythia/plugins/weather_forecast_simple/__init__.py @@ -18,28 +18,33 @@ def initialize(config, plugins, full_config): logging.info("[SIMPLE WEATHER FORECAST PLUGIN] Initializing plugin") - config["weatherDir"] = full_config["weatherDir"] - config["start_date"] = pythia.util.to_julian_date( - pythia.util.from_iso_date(config["start_date"]) + cfg = config["params"] + cfg["weatherDir"] = full_config["weatherDir"] + cfg["start_date"] = pythia.util.to_julian_date( + pythia.util.from_iso_date(cfg["start_date"]) ) - config["end_date"] = pythia.util.to_julian_date(pythia.util.from_iso_date(config["end_date"])) - config["start_on"] = config["start_date"][2:5] - config["end_on"] = config["end_date"][2:5] + cfg["end_date"] = pythia.util.to_julian_date( + pythia.util.from_iso_date(cfg["end_date"]) + ) + cfg["start_on"] = cfg["start_date"][2:5] + cfg["end_on"] = cfg["end_date"][2:5] return register_plugin_function( - PluginHook.post_build_context, construct_pixel_forecast, config, plugins + PluginHook.post_build_context, construct_pixel_forecast, cfg, plugins ) def construct_pixel_forecast(config={}, context={}): """NOTE: This function is NOT side-effect free. It does I/O to create a new file in the - context["contextWorkDir"]. But this side-effect also is intentional to interrupt the - creation of the symlinks further along the process.""" + context["contextWorkDir"]. But this side-effect also is intentional to interrupt the + creation of the symlinks further along the process.""" """TODO: Fix the wrap around case for leap years""" logging.debug("[SIMPLE WEATHER FORECAST PLUGIN] Running construct_pixel_forecast()") source_weather = os.path.join(config["weatherDir"], context["wthFile"]) - dest_weather = os.path.join(context["contextWorkDir"], "{}.WTH".format(config["wsta"])) + dest_weather = os.path.join( + context["contextWorkDir"], "{}.WTH".format(config["wsta"]) + ) target_lines = [] with open(source_weather) as source: scraping_lines = False @@ -52,7 +57,7 @@ def construct_pixel_forecast(config={}, context={}): scraping_lines = False break - with open(source_weather) as source, open(dest_weather, "w") as dest: + with open(source_weather) as source, open(dest_weather, "w", newline = '') as dest: in_target = False wrote_target = False for line in source: diff --git a/pythia/rescale.py b/pythia/rescale.py new file mode 100644 index 00000000..591e8e16 --- /dev/null +++ b/pythia/rescale.py @@ -0,0 +1,321 @@ +__license__ = "BSD-3-Clause" + +import json +import logging +import os + +import fiona +import rasterio +from rasterio.warp import reproject + +import pythia.functions + + +def get_raster_resolution(file_path): + with rasterio.open(file_path, 'r') as raster: + return [round(raster.profile['transform'][0], 6), round(raster.profile['transform'][4], 6)] + +def get_vector_resolution(file_path): + with fiona.open(file_path, 'r') as src: + min_x_dist, min_y_dist = None, None + if src[0]['geometry']['type'] == 'Polygon': + x_min, y_min, x_max, y_max = None, None, None, None + for coords in src[0]['geometry']['coordinates']: + if x_min is None or coords[0] < x_min: + x_min = coords[0] + if x_max is None or x_max < coords[0]: + x_max = coords[0] + if y_min is None or coords[1] < y_min: + y_min = coords[1] + if y_max is None or y_max < coords[1]: + y_max = coords[1] + x_dist = x_max - x_min + y_dist = y_max - y_min + return (x_dist, y_dist) + else: + points = [feature['geometry']['coordinates'] for feature in src] + for i in range(len(points) - 1): + x1, y1 = points[i] + x2, y2 = points[i + 1] + x_diff = abs(x2 - x1) # Can we add a round() here and... + y_diff = abs(y2 - y1) # here? + if x_diff > 0: + if min_x_dist is None or x_diff < min_x_dist: + min_x_dist = x_diff + if y_diff > 0: + if min_y_dist is None or y_diff < min_y_dist: + min_y_dist = y_diff + if (min_x_dist not in (None, 0)) and (min_y_dist not in (None, 0)): + return [(min_x_dist), (min_y_dist)] + else: + return None, None + + +def change_raster_resolution(file_path, scale_factor, config, dst_path=None, need_val_scaling=False): + file_name = file_path.split('/')[-1].split('.')[0] + + with rasterio.open(file_path, 'r') as src: + profile = src.profile + + # Use resampling method defined + resampling_mode = rasterio.enums.Resampling.nearest + + # Define the new resolution + new_width = int(profile['width'] * scale_factor) + new_height = int(profile['height'] * scale_factor) + + # Compute the new transform + transform = profile['transform'] * profile['transform'].scale( + profile['width'] / new_width, + profile['height'] / new_height + ) + + # Update the profile for the new raster + profile.update({ + 'height': new_height, + 'width': new_width, + 'transform': transform + }) + + rescale_dir = config['workDir'] + rescale_dir += "/rescale/" if rescale_dir[-1] != '/' else 'rescale/' + new_filename = file_name + '_rescaled.tif' + dst_path = rescale_dir + new_filename if dst_path is None else dst_path + + try: + os.makedirs(rescale_dir, exist_ok=True) + except OSError as e: + logging.error( + "OSError encountered when attempting to create rescale_dir: " + str(e) + ) + raise e + + with rasterio.open(dst_path, 'w', **profile) as dst: + for i in range(1, profile['count'] + 1): + src_array = src.read(i) + + dst_array = rasterio.band(dst, i).read(1) + reproject( + source=src_array, + destination=dst_array, + src_transform=src.transform, + src_crs=src.crs, + dst_transform=transform, + dst_crs=src.crs, + resampling=resampling_mode + ) + + if need_val_scaling: + area_scaling_factor = (1 / scale_factor) ** 2 + dst_array *= area_scaling_factor + + dst.write(dst_array, i) + + return dst_path + + + +def change_vector_resolution(file_path, scale_factor, config, current_res, dst_path=None): + """ + Rescale the original file by a defined factor. + + :param upscaling_factor: Must be an integer >= 2. + 2 will result in a 2x2 grid of points created where 1 used to be. + + :return: + """ + file_name = file_path.split('/')[-1].split('.')[0] + + with fiona.open(file_path, 'r') as src: + components = [abs(component_res) for component_res in current_res] + d = max(components) + + new_features = [] + id = 0 + + rescale_dir = config['workDir'] + rescale_dir += "/rescale/" if rescale_dir[-1] != '/' else 'rescale/' + new_filename = file_name + '_rescaled.shp' + dst_path = rescale_dir + new_filename if dst_path is None else dst_path + + try: + os.makedirs(rescale_dir, exist_ok=True) + except OSError as e: + logging.error( + "OSError encountered when attempting to create rescale_dir: " + str(e) + ) + raise e + + with fiona.open(dst_path, 'w', + crs=src.crs, + driver="ESRI Shapefile", + schema=src.schema) as dst: + for feature in src: + if feature['geometry']['type'] == 'Point': + x_old = feature['geometry']['coordinates'][0] + y_old = feature['geometry']['coordinates'][1] + + old_properties = feature['properties'] + + for i in range(scale_factor): + for j in range(scale_factor): + x_new = x_old + d * (2 * i - scale_factor + 1) / (2 * scale_factor) + y_new = y_old + d * (2 * j - scale_factor + 1) / (2 * scale_factor) + + # Because of impending deprecation in fiona, new Geometry, Properties + # and Feature objs must be made + new_geometry = fiona.Geometry((x_new, y_new), type='Point') + + # TODO: find out how slimmed down we can get the properties (what is necessary) + properties = {} + [properties.update({key: value}) for key, value in old_properties.items() if key != 'id'] + + new_properties = fiona.Properties.from_dict(properties) + + dst.write(fiona.Feature(new_geometry, str(id), new_properties)) + id += 1 + return dst_path + + +def get_desired_res(run, desired='highest'): + desired = desired.lower() + if desired not in ['highest', 'high', 'lowest', 'low']: + raise ValueError('desired must be one of \"highest, high, lowest, low\"') + + target_res = [None, None] + if desired in ['highest', "high"]: + for key, value in run.items(): + if target_res[0] is None or abs(value[0]) < abs(target_res[0]): + target_res[0] = value[0] + if target_res[1] is None or abs(value[1]) < abs(target_res[1]): + target_res[1] = value[1] + elif desired in ['lowest', "low"]: + for key, value in run.items(): + if target_res[0] is None or abs(value[0]) > abs(target_res[0]): + target_res[0] = value[0] + if target_res[1] is None or abs(value[1]) > abs(target_res[1]): + target_res[1] = value[1] + + return target_res + + +def assign_scale_factors(target_res, run): + for value in run.values(): + value.append(abs(round(value[0] / target_res[0], 1))) + value.append(abs(round(value[1] / target_res[1], 1))) + + +def change_resolutions(config, resolutions): + for run in resolutions['runs']: + for key, value in run.items(): + if value[4] != 1 or value[5] != 1: + if value[2] == 'vector': + updated_path = change_vector_resolution(value[3], int(value[4]), config, (value[0], value[1])) + value[3] = updated_path + elif value[2] == 'raster': + # Hardcoded harvestArea rescaling of value. + if key == "harvestArea": + updated_path = change_raster_resolution(value[3], value[4], config) + else: + updated_path = change_raster_resolution(value[3], value[4], config) + value[3] = updated_path + + +def alter_config(config, resolutions): + for i in range(len(resolutions['runs'])): + for key, value in resolutions['runs'][i].items(): + if key == "wsta" or config['runs'][i][key].split('::')[0] == "lookup_wth": + config_val = config['runs'][i][key].split('::') + config_val[-2] = value[3] + config['runs'][i][key] = '::'.join(config_val) + else: + config_val = config['runs'][i][key].split('::') + config_val[-1] = value[3] + config['runs'][i][key] = '::'.join(config_val) + + +def execute(config, plugins): + """ + Keep track of all the resolutions and what they point to. + To do this, create a template dictionary containing all of the files. + None values indicate a placeholder. + """ + # Create runs sub-dictionary with the same number of runs as the orginal + resolutions = {"runs": [{key: None for key in run.keys()} for run in config['runs']]} + + # print(json.dumps(resolutions, indent=4)) + + # Create a sub-dictionary for fertilizers since an arbitrary number of arguments may be used + # Only add values if they start in $, this indicates it is an arg (may or may not be file) + for i in range(len(config['runs'])): + fertilizer_dict = {arg[1:]: None for arg in config['runs'][i]['fertilizers'].split("::")[1:] if arg[0] == '$'} + resolutions["runs"][i].update(fertilizer_dict) + + # Find file path of a key + # sites + i = 0 + sites_functions = [pythia.functions.xy_from_vector.__name__] + for run in config['runs']: + site_path = run['sites'].split("::")[1] if run['sites'].split("::")[0] in sites_functions else None + try: + resolutions['runs'][i]['sites'] = get_vector_resolution(site_path) if site_path is not None else None + resolutions['runs'][i]['sites'].append('vector') + resolutions['runs'][i]['sites'].append(site_path) + except fiona.errors.DriverError: + logging.error( + "Specified file path for \"sites\" does not point to a vector file. Path: %s", + site_path + ) + i += 1 + + to_delete = [] + # Iterate through every run in the config file + for i in range(len(config['runs'])): + for key, value in config['runs'][i].items(): + if key in ["sites"]: + continue + elif isinstance(value, str): + try: + raster_pos = value.split('::').index('raster') + try: + resolutions['runs'][i][key] = get_raster_resolution(value.split("::")[raster_pos + 1]) + resolutions['runs'][i][key].append('raster') + resolutions['runs'][i][key].append(value.split("::")[raster_pos + 1]) + except rasterio.errors.RasterioIOError: + logging.error( + "Specified file path for \"%s\" does not point to a raster file. Path: %s", + key, value.split("::")[raster_pos + 1] + ) + except ValueError: + try: + vector_pos = value.split('::').index('vector') + try: + resolutions['runs'][i][key] = get_vector_resolution(value.split("::")[vector_pos + 1]) + resolutions['runs'][i][key].append('vector') + resolutions['runs'][i][key].append(value.split("::")[vector_pos + 1]) + except fiona.errors.DriverError: + logging.error( + "Specified file path for \"%s\" does not point to a raster file. Path: %s", + key, + value.split("::")[vector_pos + 1] + ) + except ValueError: + if key not in to_delete: + to_delete.append(key) + else: + if key not in to_delete: + to_delete.append(key) + + # Remove non-file keys from resolutions dict + for key in to_delete: + for i in range(len(config['runs'])): + del resolutions['runs'][i][key] + + for run in resolutions['runs']: + assign_scale_factors(get_desired_res(run, "highest"), run) + change_resolutions(config, resolutions) + + alter_config(config, resolutions) + + # print(json.dumps(config, indent=4)) + return config diff --git a/pythia/soil_handler.py b/pythia/soil_handler.py index 63589393..b7ce6f46 100644 --- a/pythia/soil_handler.py +++ b/pythia/soil_handler.py @@ -1,3 +1,5 @@ +__license__ = "BSD-3-Clause" + def findSoilProfile(profile, soilFiles): profile = "*{}".format(profile) for sf in soilFiles: @@ -157,4 +159,5 @@ def calculateICLayerData(soilData, run): calculateH2O(run["icsw%"], slll, sdul), [icnd * 0.1 for icnd in icndist], [icnd * 0.9 for icnd in icndist], - ]) + ] + ) diff --git a/pythia/template.py b/pythia/template.py index 36f53dd9..8e57d082 100644 --- a/pythia/template.py +++ b/pythia/template.py @@ -1,4 +1,8 @@ +__license__ = "BSD-3-Clause" + from jinja2 import Environment, FileSystemLoader +import logging +import pythia.functions import pythia.util _t_formats = { @@ -8,13 +12,6 @@ "id_soil": {"align": ":<", "length": 10}, "xcrd": {"fmt": ":>15.3f"}, "ycrd": {"fmt": ":>15.3f"}, - "icrt": {"length": 6}, - "icres": {"length": 6}, - "icren": {"length": 6}, - "icbl": {"length": 6}, - "sh2o": {"length": 6}, - "snh4": {"length": 6}, - "sno3": {"length": 6}, "fdate": {"length": 5}, "fdap": {"length": 5}, "famn": {"length": 5}, @@ -24,53 +21,126 @@ "flhst": {"length": 5}, "fhdur": {"length": 5}, "irrig": {"length": 5}, - "erain": {"raw": "M{:>4}"}, - "ph2ol": {"length": 5} + "ph2ol": {"length": 5}, + "fodate": {"length": 7}, + "pdate": {"length": 5}, + "pfrst": {"length": 5}, + "plast": {"length": 5}, + "hdate": {"length": 5}, + "ppop": {"length": 5}, + "plrs": {"length": 3}, + + # Initial Conditions (Soil Profiles) + "icbl": {"length": 6}, + "sh2o": {"fmt": ":>6.3f"}, + "snh4": {"fmt": ":>6.2f"}, + "sno3": {"fmt": ":>6.2f"}, + + # Initial Conditions (Residue) + "icrt": {"length": 6}, + "icnd": {"length": 6}, + "icrn": {"length": 6}, + "icre": {"length": 6}, + "icwd": {"length": 6}, + "icres": {"length": 6}, + "icren": {"length": 6}, + "icrep": {"length": 6}, + "icrip": {"length": 6}, + "icrid": {"length": 6}, + + # Soil Analysis + "sadat": {"length": 5}, + "smhb": {"length": 6}, + "smpx": {"length": 6}, + "smke": {"length": 6}, } -_t_date_fields = ["sdate", "fdate", "pfrst", "plast", "pdate"] +_t_date_fields = ["sdate", "fdate", "pfrst", "plast", "pdate", "hdate"] +_t_date_fields_4 = ["fodate"] +_t_envmod_fields = ["eday", "erad", "emax", "emin", "erain", "eco2", "edew", "ewind"] def init_engine(template_dir): - return Environment(loader=FileSystemLoader(template_dir), trim_blocks=True, lstrip_blocks=True) + return Environment( + loader=FileSystemLoader(template_dir), trim_blocks=True, lstrip_blocks=True + ) + + +def wrap_format(k, v): + fmt = "" + if k in _t_formats: + if "raw" in _t_formats[k]: + fmt = _t_formats[k]["raw"] + elif "fmt" in _t_formats[k]: + fmt = "{" + _t_formats[k]["fmt"] + "}" + else: + fmt_align = _t_formats[k].get("align", ":>") + fmt_pad = _t_formats[k].get("pad_with", "") + if isinstance(v, float): + fmt_len = "{}.1f".format(_t_formats[k]["length"]) + elif isinstance(v, int): + fmt_len = "{}d".format(_t_formats[k]["length"]) + else: + if "length" in _t_formats[k]: + fmt_len = "{}".format(_t_formats[k]["length"]) + else: + fmt_len = "" + fmt = "{" + fmt_align + fmt_pad + fmt_len + "}" + else: + fmt = "{}" + return fmt.format(v) + + +def envmod_format(v): + fmt = "{}{:>4}" + valid_mod = ["R", "A", "M", "S"] + try: + mod, raw = v[0], v[1:] + except TypeError: + logging.error("%s is not a valid value for envmod.", v) + return fmt.format("A", "0") + if mod not in valid_mod: + logging.error('"%s" is not a valid envmod modifier.', mod) + return fmt.format("A", "0") + val = pythia.functions.string_to_number(raw) + if val is None: + return fmt.format("A", "0") + return "{}{:>4}".format(mod, val) def auto_format_dict(d): if isinstance(d, str): return d clean = {} + for k in _t_formats: + v = _t_formats[k] + if "default" in v: + clean[k] = wrap_format(k, v["default"]) + else: + clean[k] = wrap_format(k, -99) + for k in _t_envmod_fields: + clean[k] = envmod_format("A0") for k, v in d.items(): - if k in _t_date_fields and "::" not in v: - clean[k] = pythia.util.to_julian_date(pythia.util.from_iso_date(v)) - elif k in _t_formats: - fmt = "" - if "raw" in _t_formats[k]: - fmt = _t_formats[k]["raw"] - elif "fmt" in _t_formats[k]: - fmt = "{" + _t_formats[k]["fmt"] + "}" - else: - fmt_align = _t_formats[k].get("align", ":>") - fmt_pad = _t_formats[k].get("pad_with", "") - if isinstance(v, float): - fmt_len = "{}.1f".format(_t_formats[k]["length"]) - elif isinstance(v, int): - fmt_len = "{}d".format(_t_formats[k]["length"]) + if v == "-99" or v == -99: + clean[k] = wrap_format(k, v) + else: + if k in _t_date_fields and "::" not in v: + clean[k] = pythia.util.to_julian_date(pythia.util.from_iso_date(v)) + elif k in _t_date_fields_4 and "::" not in v: + clean[k] = pythia.util.to_julian_date_4(pythia.util.from_iso_date(v)) + elif k in _t_envmod_fields: + clean[k] = envmod_format(v) + elif k in _t_formats: + clean[k] = wrap_format(k, v) + elif isinstance(v, dict): + clean[k] = auto_format_dict(v) + elif isinstance(v, list) and not isinstance(v, str): + if k == "sites": + continue else: - if "length" in _t_formats[k]: - fmt_len = "{}".format(_t_formats[k]["length"]) - else: - fmt_len = "" - fmt = "{" + fmt_align + fmt_pad + fmt_len + "}" - clean[k] = fmt.format(v) - elif isinstance(v, dict): - clean[k] = auto_format_dict(v) - elif isinstance(v, list) and not isinstance(v, str): - if k == "sites": - continue + clean[k] = [auto_format_dict(intern) for intern in v] else: - clean[k] = [auto_format_dict(intern) for intern in v] - else: - clean[k] = v + clean[k] = v return clean diff --git a/pythia/tests/plugin_test.py b/pythia/tests/plugin_test.py index b577c830..fa197a57 100644 --- a/pythia/tests/plugin_test.py +++ b/pythia/tests/plugin_test.py @@ -1,4 +1,9 @@ -from pythia.plugin import PluginHook, register_plugin_function, load_plugins, run_plugin_functions +from pythia.plugin import ( + PluginHook, + register_plugin_function, + load_plugins, + run_plugin_functions, +) def test_register_with_invalid_hook(): @@ -9,13 +14,17 @@ def test_register_with_invalid_hook(): def test_register_with_invalid_fun(): plugins = {} - plugins1 = register_plugin_function(PluginHook.analyze_file, "not a function", "", plugins) + plugins1 = register_plugin_function( + PluginHook.pre_analytics, "not a function", "", plugins + ) assert plugins1 == {} def test_register_with_invalid_config(): plugins = {} - plugins1 = register_plugin_function(PluginHook.post_analysis, sample_function, "", plugins) + plugins1 = register_plugin_function( + PluginHook.post_analytics, sample_function, "", plugins + ) assert plugins1 == {} @@ -23,19 +32,27 @@ def test_register_twice(): plugins = {} plugins1 = {} plugins2 = {} - plugins1 = register_plugin_function(PluginHook.analyze_file, sample_function, {}, plugins) + plugins1 = register_plugin_function( + PluginHook.pre_analytics, sample_function, {}, plugins + ) plugins2 = register_plugin_function( - PluginHook.analyze_file, sample_function, {"a": 1}, plugins1 + PluginHook.pre_analytics, sample_function, {"a": 1}, plugins1 ) - assert plugins1 == {PluginHook.analyze_file: [{"fun": sample_function, "config": {}}]} + assert plugins1 == { + PluginHook.pre_analytics: [{"fun": sample_function, "config": {}}] + } assert plugins1 == plugins2 def test_register_properly(): plugins = {} plugins1 = {} - plugins1 = register_plugin_function(PluginHook.post_build_context, sample_function, {}, plugins) - assert plugins1 == {PluginHook.post_build_context: [{"fun": sample_function, "config": {}}]} + plugins1 = register_plugin_function( + PluginHook.post_build_context, sample_function, {}, plugins + ) + assert plugins1 == { + PluginHook.post_build_context: [{"fun": sample_function, "config": {}}] + } def sample_function(config, context): @@ -53,7 +70,9 @@ def test_load_plugin(): assert plugins1[PluginHook.post_config] == [ {"fun": pythia.plugins.test_plugin.sample_function, "config": {}} ] - assert plugins1 != {PluginHook.post_config: [{"fun": sample_function, "config": {}}]} + assert plugins1 != { + PluginHook.post_config: [{"fun": sample_function, "config": {}}] + } def test_plugin_manual_execution(): @@ -72,8 +91,10 @@ def test_plugin_auto_execution(): plugins1 = {} plugins1 = load_plugins(config, plugins) context = {"context_value": 7} - context1 = run_plugin_functions(PluginHook.post_build_context, plugins1, context=context) - context2 = run_plugin_functions(PluginHook.post_build_context, plugins1) + context1 = run_plugin_functions( + PluginHook.post_build_context, plugins1, context=context + ).get("context") + context2 = run_plugin_functions(PluginHook.post_build_context, plugins1).get("context", None) assert context1 != context assert context1["context_value"] == 8 assert context2["context_value"] == 3 @@ -84,8 +105,12 @@ def test_no_plugin_does_not_change_context(): plugins = {} plugins1 = load_plugins(config, plugins) context = {"hello": "there"} - context1 = run_plugin_functions(PluginHook.post_build_context, plugins, context=context) + context1 = run_plugin_functions( + PluginHook.post_build_context, plugins, context=context + ).get("context") assert context == context1 - context2 = run_plugin_functions(PluginHook.post_build_context, plugins1, context=context) + context2 = run_plugin_functions( + PluginHook.post_build_context, plugins1, context=context + ).get("context") assert context1 != context2 - assert(context2 == {**context, **{"context_value": 3}}) + assert context2 == {**context, **{"context_value": 3}} diff --git a/pythia/util.py b/pythia/util.py index 5d100320..7186704f 100644 --- a/pythia/util.py +++ b/pythia/util.py @@ -1,25 +1,53 @@ +__license__ = "BSD-3-Clause" + import datetime +import logging import pythia.functions def to_julian_date(d): - return d.strftime("%y%j") + try: + return d.strftime("%y%j") + except ValueError: + logging.error("Unable to convert %s to a julian date", d) + return None + + +def to_julian_date_4(d): + try: + return d.strftime("%Y%j") + except ValueError: + logging.error("Unable to convert %s to a julian date", d) + return None def to_iso_date(d): - return d.strftime("%Y-%m-%d") + try: + return d.strftime("%Y-%m-%d") + except ValueError: + logging.error("Unable to convert %s to an ISO date", d) + return None def from_julian_date(s): - return datetime.datetime.strptime(s, "%y%j").date() + try: + return datetime.datetime.strptime(s, "%y%j").date() + except ValueError: + pass + try: + return datetime.datetime.strptime(s, "%Y%j").date() + except ValueError: + logging.error('"%s" is an invalid julian date format.', s) + return None def from_iso_date(s): try: return datetime.datetime.strptime(s, "%Y-%m-%d").date() except ValueError: - pass + logging.error('"%s" is an invalid ISO date format.', s) + return None def get_rasters_list(iterator): @@ -43,13 +71,13 @@ def get_rasters_dict(iterator): def translate_coords_news(lat, lng): if lat >= 0: - y = "{:.3f}N".format(lat).replace(".", "_") + y = "{:.4f}N".format(lat).replace(".", "_") else: - y = "{:.3f}S".format(abs(lat)).replace(".", "_") + y = "{:.4f}S".format(abs(lat)).replace(".", "_") if lng >= 0: - x = "{:.3f}E".format(lng).replace(".", "_") + x = "{:.4f}E".format(lng).replace(".", "_") else: - x = "{:.3f}W".format(abs(lng)).replace(".", "_") + x = "{:.4f}W".format(abs(lng)).replace(".", "_") return y, x diff --git a/sample.json b/sample.json index 03227d3e..da7ca445 100644 --- a/sample.json +++ b/sample.json @@ -37,7 +37,8 @@ "ic_layers": "generate_ic_layers::$id_soil", "ramt": 0, "fen_tot": 100.0, - "fertilizers": "split_fert_dap_percent::$fe" + "fertilizers": "split_fert_dap_percent::$fe", + "population": "raster::data/rasters/population.tif" }, "dssat": { "executable": "/usr/local/dssat47/dscsm047" @@ -53,6 +54,7 @@ "LATITUDE", "LONGITUDE", "HARVEST_AREA", + "POPULATION", "RUN_NAME", "CR", "PDAT", @@ -72,4 +74,4 @@ "harvestArea": "raster::data/rasters/unity_spam_harvest_sorghum.tif" } ] -} \ No newline at end of file +} diff --git a/tox.ini b/tox.ini index 6deafc26..68151ede 100644 --- a/tox.ini +++ b/tox.ini @@ -1,2 +1,4 @@ [flake8] -max-line-length = 120 +max-line-length = 160 +[pycodestyle] +max-line-length = 160 diff --git a/v1/__init__.py b/v1/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/v1/pythia.py b/v1/pythia.py deleted file mode 100644 index dbd238a6..00000000 --- a/v1/pythia.py +++ /dev/null @@ -1,82 +0,0 @@ -import argparse -from datetime import datetime -import math -import os -import numpy as np -import pandas as pd -import rasterio - -def loadRaster(raster): - data = {} - with rasterio.open(raster) as r: - data['nd_val'] = r.nodata - data['vals'] = r.read(1) - return data - -def loadSummary(d, idx, items): - df = pd.read_csv(os.path.join(d, 'summary.csv'), index_col=False) - lim = df.filter(items=items, col_index) - lim.replace(-99, 0, inplace=True) - lim.fillna(0, inplace=True) - lim['adj'] = np.where((lim['ADAT'] == 0) | (lim['HDAT'] == 0), 1, 0) - lim.loc[lim['ADAT'] == 0, 'HDAT'] = 0 - df = None - return lim - -def applyScale(df, scale): - return df*scale - -def applyAverage(df, col, cum): - return round(df[col]/cum) - -def applyDateAverage(df, col, cum): - return df[col]/(cum - df['adj']) - -def accumulate(acc, df): - if acc is None: - return df - else: - return acc+df - -def main(args): - scale_data = loadRaster(args.scale) - directories = next(os.walk(args.wd))[1] -# coord_list = [tuple(map(int, d.split('_'))) for d in directories] - scale_factors = ([scale_data['vals'][c[0]][c[1]] for c in [tuple(map(int, d.split('_'))) for d in directories]]) - sf_cum = sum(scale_factors) - if args.debug: - print(list(zip(directories, scale_factors))) - print(sf_cum) - acc = None - scale_adjust = {} - for idx, d in enumerate(directories): - if args.debug: - print("Running {}".format(d)) - summary = loadSummary(os.path.join(args.wd, d)) - scaled = applyScale(summary, scale_factors[idx]) - acc = accumulate(acc, scaled) - if args.debug: - print(acc['adj']) - acc['HWAM'] = (acc['HWAM'].astype(int))/1000 #hardcoded to Metric Tonnes - acc['PRCP'] = applyDateAverage(acc, 'PRCP', sf_cum).astype(int) - acc['AY'] = round(acc['HWAM']/sf_cum).astype(int) - acc['PDAT'] = pd.to_datetime(applyAverage(acc, 'PDAT', sf_cum).apply(math.floor).apply(str), format="%Y%j", errors='coerce') - acc['ADAT'] = pd.to_datetime(applyDateAverage(acc, 'ADAT', sf_cum).apply(math.floor).apply(str), format="%Y%j", errors='coerce') - acc['HDAT'] = pd.to_datetime(applyDateAverage(acc, 'HDAT', sf_cum).apply(math.floor).apply(str), format="%Y%j", errors='coerce') - acc['YEAR'] = acc.index+1984 - acc['SCALE'] = round((acc['adj']/sf_cum)*100).astype(int) - acc = acc.reindex(columns=['YEAR', 'HWAM', 'AY', 'PRCP', 'PDAT', 'ADAT', 'HDAT', 'SCALE']) - acc.columns = ['Year', 'Production (t)', 'Average Yield (kg/ha)', 'Average Rainfall (mm)', 'Average Planting Date', 'Average Anthesis Date', 'Average Harvest Date', 'Percent Failures'] - if args.debug or args.no_output: - print(acc) - if not args.no_output: - acc.to_csv(os.path.join(args.wd, 'overview.csv'), index=False) - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='The oracle') - parser.add_argument('wd', help='The working directory') - parser.add_argument('scale', help='The raster used to scale up the results') - parser.add_argument('--no-output', action='store_true') - parser.add_argument('--debug', action='store_true') - args = parser.parse_args() - main(args)