├── .github └── workflows │ ├── deps.yml │ ├── nextrelease.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── importmap ├── __init__.py ├── apps.py ├── core.py ├── generator.py ├── management │ └── commands │ │ └── importmap_generate.py ├── models.py ├── templates │ └── importmap │ │ └── scripts.html └── templatetags │ ├── __init__.py │ └── importmap.py ├── poetry.lock ├── pyproject.toml ├── scripts ├── format ├── install ├── pre-commit └── test └── test_project ├── app ├── apps.py ├── jinja2.py ├── models.py ├── static │ └── app.js ├── templates │ └── index.html └── urls.py ├── importmap.lock ├── manage.py ├── pyproject.toml ├── settings.py ├── templates └── jinja2 │ └── index_jinja.html ├── tests.py └── wsgi.py /.github/workflows/deps.yml: -------------------------------------------------------------------------------- 1 | name: deps 2 | 3 | on: 4 | schedule: 5 | - cron: 0 0 1 * * 6 | workflow_dispatch: {} 7 | 8 | jobs: 9 | deps: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - id: generate_token 13 | uses: tibdex/github-app-token@v1 14 | with: 15 | app_id: ${{ secrets.DEPS_GITHUB_APP_ID }} 16 | private_key: ${{ secrets.DEPS_GITHUB_APP_KEY }} 17 | - uses: actions/checkout@v2 18 | with: 19 | token: ${{ steps.generate_token.outputs.token }} 20 | - uses: actions/setup-python@v2 21 | with: 22 | python-version: 3.x 23 | - run: | 24 | pip install -U pip poetry 25 | ./scripts/install 26 | - run: curl https://deps.app/install.sh | bash -s -- -b $HOME/bin 27 | - run: $HOME/bin/deps ci 28 | env: 29 | DEPS_TOKEN: ${{ secrets.DEPS_TOKEN }} 30 | DEPS_GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} 31 | -------------------------------------------------------------------------------- /.github/workflows/nextrelease.yml: -------------------------------------------------------------------------------- 1 | name: nextrelease 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | branches: [master] 7 | types: [labeled, unlabeled, edited, synchronize] 8 | 9 | jobs: 10 | sync: 11 | if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && github.head_ref == 'nextrelease' }} 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: dropseed/nextrelease@v2 15 | env: 16 | POETRY_PYPI_TOKEN_PYPI: ${{ secrets.DROPSEED_PYPI_TOKEN }} 17 | with: 18 | prepare_cmd: | 19 | sed -i -e "s/version = \"[^\"]*\"$/version = \"$VERSION\"/g" pyproject.toml 20 | publish_cmd: | 21 | pip3 install -U pip poetry && poetry publish --build --no-interaction 22 | github_token: ${{ secrets.GITHUB_TOKEN }} 23 | release_notes: generate 24 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-python@v2 14 | with: 15 | python-version: ${{ matrix.python-version }} 16 | - name: Install dependencies 17 | run: | 18 | pip install poetry 19 | ./scripts/install 20 | - name: Check formatting and test 21 | run: | 22 | ./scripts/pre-commit 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /.venv 3 | *.pyc 4 | __pycache__ 5 | db.sqlite3 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dropseed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > Development on this package has moved to [Plain](https://plainframework.com?ref=django-importmap) and will continue there. 3 | > Existing releases will remain published and I encourage you to fork this repo if you need to make changes. 4 | 5 | # django-importmap 6 | 7 | Heavily inspired by [rails/importmap-rails](https://github.com/rails/importmap-rails), 8 | this app adds a simple process for integrating [import maps](https://github.com/WICG/import-maps) into Django. 9 | 10 | This is a new project and it hasn't been used in production yet. 11 | But if you're looking to use import maps with Django, give it a try and tell us how it goes. 12 | The structure (and code) is pretty simple. 13 | Contributions are welcome! 14 | 15 | ## How to use it 16 | 17 | You'll need to do four things to use django-importmap. 18 | 19 | The TL;DR is: 20 | 21 | - Add "importmap" to `INSTALLED_APPS` 22 | - Create an `importmap.toml` 23 | - Run `python manage.py importmap_generate` 24 | - Use `{% importmap_scripts %}` in your template 25 | 26 | ### 1. Install it 27 | 28 | Do the equivalent of `pip install django-importmap` and add it to your `INSTALLED_APPS` list in your `settings.py` file. 29 | 30 | ```python 31 | # settings.py 32 | INSTALLED_APPS = [ 33 | ... 34 | "importmap", 35 | ] 36 | ``` 37 | 38 | ### 2. Configuring an import map 39 | 40 | You JavaScript dependencies are conveniently located in your`pyproject.toml` file. 41 | 42 | They are listed under `[tool.importmap.dependencies]` and you can add them there. The format is `name = "version"`, 43 | similar to how you would add a dependency to your `package.json` file. 44 | 45 | ```toml 46 | # pyproject.toml 47 | [tool.importmap.dependencies] 48 | react = "17.0.2" 49 | react-dom = "17.0.2" 50 | ``` 51 | 52 | [jspm.org generator](https://jspm.org/docs/api#install) is used lock and serve the dependencies, 53 | but is basically just like installing them via `npm i @`. 54 | 55 | ### 3. Run `importmap_generate` 56 | 57 | To resolve the import map, you'll need to run `python manage.py importmap_generate`. 58 | 59 | This will create `importmap.lock` (which you should save and commit to your repo) that contains the actual import map JSON (both for development and production). 60 | 61 | You don't need to look at this file yourself, but here is an example of what it will contain: 62 | 63 | ```json 64 | { 65 | "config_hash": "09d6237cdd891aad07de60f54689d130", 66 | "importmap": { 67 | "imports": { 68 | "react": "https://ga.jspm.io/npm:react@17.0.2/index.js" 69 | }, 70 | "scopes": { 71 | "https://ga.jspm.io/": { 72 | "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js" 73 | } 74 | } 75 | }, 76 | "importmap_dev": { 77 | "imports": { 78 | "react": "https://ga.jspm.io/npm:react@17.0.2/dev.index.js" 79 | }, 80 | "scopes": { 81 | "https://ga.jspm.io/": { 82 | "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js" 83 | } 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | ### 4. Add the scripts to your template 90 | 91 | The import map itself gets added by using `{% load importmap %}` and then `{% importmap_scripts %}` in the head of your HTML. This will include the [es-module-shim](https://github.com/guybedford/es-module-shims). 92 | 93 | After that, you can include your own JavaScript! 94 | This could be inline or from `static`. 95 | Just be sure to use `type="module"` and the "name" you provided when doing your JS imports (i.e. "react"). 96 | 97 | ```html 98 | {% load importmap %} 99 | 100 | 101 | 102 | {% importmap_scripts %} 103 | 108 | 109 | 110 | 111 | 112 | 113 | ``` 114 | 115 | When it renders you should get something like this: 116 | 117 | ```html 118 | 119 | 120 | 121 | 122 | 134 | 135 | 140 | 141 | 142 | 143 | 144 | 145 | ``` 146 | 147 | ## Adding static files to import maps 148 | 149 | You can include your own static files in the import map by passing kwargs to the `{% importmap_scripts %}` tag. 150 | You can actually use this to include any additional imports, but by using `{% static "name" as name_static %}` you can get the URL to the static file. 151 | 152 | ```html 153 | {% load importmap static %} 154 | 155 | 156 | 157 | {% static "my-script.js" as my_script_static %} 158 | {% importmap_scripts myscript=my_script_static %} 159 | 162 | 163 | 164 | 165 | 166 | 167 | ``` 168 | 169 | ## Using Jinja2 170 | 171 | To use django-importmap with Jinja2 templates, 172 | you can add `importmap` to a customized Jinja environment. 173 | 174 | ```python 175 | TEMPLATES = [ 176 | { 177 | "BACKEND": "django.template.backends.jinja2.Jinja2", 178 | ... 179 | "OPTIONS": { 180 | "environment": "app.jinja2.environment", 181 | ... 182 | }, 183 | } 184 | ] 185 | ``` 186 | 187 | Then in `app/jinja2.py`: 188 | 189 | ```python 190 | from django.conf import settings 191 | from jinja2 import Environment 192 | 193 | from importmap import Importmap 194 | 195 | 196 | def environment(**options): 197 | env = Environment(**options) 198 | env.globals.update({"importmap": Importmap.json(development=settings.DEBUG)}) 199 | return env 200 | ``` 201 | 202 | Then in your Jinja templates you can include a module shim and output the `importmap` variable like this: 203 | 204 | ```html 205 | 206 | 207 | 208 | 209 | 212 | 216 | 217 | 218 | 219 | 220 | 221 | ``` 222 | 223 | To include your own static files in the import map, 224 | you can pass a dictionary of names and URLs to the `Importmap.json` method: 225 | 226 | ```python 227 | from django.conf import settings 228 | from django.templatetags.static import static 229 | from jinja2 import Environment 230 | 231 | from importmap import Importmap 232 | 233 | 234 | def environment(**options): 235 | env = Environment(**options) 236 | env.globals.update( 237 | { 238 | "importmap": Importmap.json( 239 | development=settings.DEBUG, extra_imports={"myjs": static("myjs.js")} 240 | ) 241 | } 242 | ) 243 | return env 244 | ``` 245 | 246 | ## Project status 247 | 248 | This is partly an experiment, 249 | but honestly it's so simple that I don't think there can be much wrong with how it works currently. 250 | 251 | Here's a list of things that would be nice to do (PRs welcome): 252 | 253 | - Command to add new importmap dependency (use `^` version automatically?) 254 | - Django check for comparing lock and config (at deploy time, etc.) 255 | - Use [deps](https://www.dependencies.io/) to update shim version 256 | - Preload option 257 | - Vendoring option (including shim) 258 | - More complete error handling (custom exceptions, etc.) 259 | -------------------------------------------------------------------------------- /importmap/__init__.py: -------------------------------------------------------------------------------- 1 | from .core import Importmap 2 | -------------------------------------------------------------------------------- /importmap/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ImportmapConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "importmap" 7 | -------------------------------------------------------------------------------- /importmap/core.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import logging 4 | import os 5 | from pathlib import Path 6 | 7 | try: 8 | import tomllib 9 | except ImportError: 10 | import tomli as tomllib 11 | 12 | from .generator import ImportmapGenerator 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | 17 | DEFAULT_CONFIG_FILENAME = "pyproject.toml" 18 | DEFAULT_LOCK_FILENAME = "importmap.lock" 19 | 20 | 21 | def hash_for_data(data): 22 | return hashlib.md5(json.dumps(data, sort_keys=True).encode("utf-8")).hexdigest() 23 | 24 | 25 | class Importmap: 26 | def __init__( 27 | self, 28 | config_filename=DEFAULT_CONFIG_FILENAME, 29 | lock_filename=DEFAULT_LOCK_FILENAME, 30 | ): 31 | self.config_file = Path(config_filename) 32 | self.lock_file = Path(lock_filename) 33 | self.load() 34 | 35 | @classmethod 36 | def json(cls, *, development=False, extra_imports={}): 37 | importmap = cls() 38 | 39 | if development: 40 | imap = importmap.map_dev 41 | indent = 2 42 | else: 43 | imap = importmap.map 44 | indent = None 45 | 46 | imap.get("imports", {}).update(extra_imports) 47 | 48 | return json.dumps(imap, indent=indent, sort_keys=True) 49 | 50 | def load(self): 51 | # TODO django check to compare lock and config hash 52 | 53 | self.config = self.load_config() 54 | 55 | if not self.config: 56 | # No config = no map and no lockfile 57 | self.map = {} 58 | self.map_dev = {} 59 | self.lock_file.unlink(missing_ok=True) 60 | return 61 | 62 | lockfile = self.load_lockfile() 63 | if lockfile: 64 | self.map = lockfile["importmap"] 65 | self.map_dev = lockfile["importmap_dev"] 66 | else: 67 | self.map = {} 68 | self.map_dev = {} 69 | 70 | def generate(self, force=False): 71 | config_hash = hash_for_data(self.config) 72 | lockfile = self.load_lockfile() 73 | if force or not lockfile or lockfile["config_hash"] != config_hash: 74 | # Generate both maps now, tag will choose which to use at runtime 75 | self.map = self.generate_map() 76 | self.map_dev = self.generate_map(development=True) 77 | 78 | lockfile["config_hash"] = config_hash 79 | lockfile["importmap"] = self.map 80 | lockfile["importmap_dev"] = self.map_dev 81 | self.save_lockfile(lockfile) 82 | 83 | def load_config(self): 84 | if not self.config_file.exists(): 85 | return {} 86 | with self.config_file.open("rb") as f: 87 | pyproject = tomllib.load(f) 88 | 89 | return pyproject["tool"]["importmap"] 90 | 91 | def load_lockfile(self): 92 | if not self.lock_file.exists(): 93 | return {} 94 | 95 | with self.lock_file.open("r") as f: 96 | return json.load(f) 97 | 98 | def save_lockfile(self, lockfile): 99 | with self.lock_file.open("w+") as f: 100 | json.dump(lockfile, f, indent=2, sort_keys=True) 101 | 102 | def generate_map(self, *args, **kwargs): 103 | return ImportmapGenerator.from_config(self.config, *args, **kwargs).generate() 104 | -------------------------------------------------------------------------------- /importmap/generator.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import requests 4 | 5 | logger = logging.getLogger(__name__) 6 | 7 | 8 | class ImportmapGeneratorError(Exception): 9 | pass 10 | 11 | 12 | class ImportmapGenerator: 13 | def __init__(self, targets, development=False, provider="jspm"): 14 | self.targets = targets 15 | self.development = development 16 | self.provider = provider 17 | 18 | @classmethod 19 | def from_config(cls, config, *args, **kwargs): 20 | return cls( 21 | [ 22 | f"{package}@{version}" 23 | for package, version in config["dependencies"].items() 24 | ], 25 | *args, 26 | **kwargs, 27 | ) 28 | 29 | def get_env(self): 30 | if self.development: 31 | return ["browser", "module", "development"] 32 | else: 33 | return ["browser", "module"] 34 | 35 | def generate(self): 36 | response = requests.post( 37 | "https://api.jspm.io/generate", 38 | json={ 39 | "install": [self.targets], 40 | "env": self.get_env(), 41 | "provider": self.provider, 42 | }, 43 | ) 44 | logger.info(response) 45 | 46 | if "error" in response.json(): 47 | raise ImportmapGeneratorError(response.json()["error"]) 48 | 49 | response.raise_for_status() 50 | 51 | return response.json()["map"] 52 | -------------------------------------------------------------------------------- /importmap/management/commands/importmap_generate.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand, CommandError 2 | 3 | from ...core import Importmap 4 | 5 | 6 | class Command(BaseCommand): 7 | help = "Generate importmap.lock" 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument("--force", action="store_true") 11 | 12 | def handle(self, *args, **options): 13 | importmap = Importmap() 14 | importmap.generate(force=options["force"]) 15 | -------------------------------------------------------------------------------- /importmap/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /importmap/templates/importmap/scripts.html: -------------------------------------------------------------------------------- 1 | {% if importmap %} 2 | 3 | 6 | {% endif %} 7 | -------------------------------------------------------------------------------- /importmap/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropseed/django-importmap/64dd5c767dd650e263d09b75c15cd9221358f080/importmap/templatetags/__init__.py -------------------------------------------------------------------------------- /importmap/templatetags/importmap.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django import template 4 | from django.conf import settings 5 | 6 | from ..core import Importmap 7 | 8 | register = template.Library() 9 | 10 | 11 | @register.inclusion_tag("importmap/scripts.html") 12 | def importmap_scripts(**extra_imports): 13 | return { 14 | "importmap": Importmap.json( 15 | development=settings.DEBUG, extra_imports=extra_imports 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "appnope" 5 | version = "0.1.3" 6 | description = "Disable App Nap on macOS >= 10.9" 7 | optional = false 8 | python-versions = "*" 9 | files = [ 10 | {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, 11 | {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, 12 | ] 13 | 14 | [[package]] 15 | name = "asgiref" 16 | version = "3.7.2" 17 | description = "ASGI specs, helper code, and adapters" 18 | optional = false 19 | python-versions = ">=3.7" 20 | files = [ 21 | {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, 22 | {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, 23 | ] 24 | 25 | [package.dependencies] 26 | typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} 27 | 28 | [package.extras] 29 | tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] 30 | 31 | [[package]] 32 | name = "asttokens" 33 | version = "2.4.1" 34 | description = "Annotate AST trees with source code positions" 35 | optional = false 36 | python-versions = "*" 37 | files = [ 38 | {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, 39 | {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, 40 | ] 41 | 42 | [package.dependencies] 43 | six = ">=1.12.0" 44 | 45 | [package.extras] 46 | astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] 47 | test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] 48 | 49 | [[package]] 50 | name = "backcall" 51 | version = "0.2.0" 52 | description = "Specifications for callback functions passed in to an API" 53 | optional = false 54 | python-versions = "*" 55 | files = [ 56 | {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, 57 | {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, 58 | ] 59 | 60 | [[package]] 61 | name = "backports-zoneinfo" 62 | version = "0.2.1" 63 | description = "Backport of the standard library zoneinfo module" 64 | optional = false 65 | python-versions = ">=3.6" 66 | files = [ 67 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, 68 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, 69 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, 70 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, 71 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, 72 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, 73 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, 74 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, 75 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, 76 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, 77 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, 78 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, 79 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, 80 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, 81 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, 82 | {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, 83 | ] 84 | 85 | [package.extras] 86 | tzdata = ["tzdata"] 87 | 88 | [[package]] 89 | name = "black" 90 | version = "22.12.0" 91 | description = "The uncompromising code formatter." 92 | optional = false 93 | python-versions = ">=3.7" 94 | files = [ 95 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 96 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 97 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 98 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 99 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 100 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 101 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 102 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 103 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 104 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 105 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 106 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 107 | ] 108 | 109 | [package.dependencies] 110 | click = ">=8.0.0" 111 | mypy-extensions = ">=0.4.3" 112 | pathspec = ">=0.9.0" 113 | platformdirs = ">=2" 114 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 115 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 116 | 117 | [package.extras] 118 | colorama = ["colorama (>=0.4.3)"] 119 | d = ["aiohttp (>=3.7.4)"] 120 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 121 | uvloop = ["uvloop (>=0.15.2)"] 122 | 123 | [[package]] 124 | name = "certifi" 125 | version = "2023.11.17" 126 | description = "Python package for providing Mozilla's CA Bundle." 127 | optional = false 128 | python-versions = ">=3.6" 129 | files = [ 130 | {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, 131 | {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, 132 | ] 133 | 134 | [[package]] 135 | name = "charset-normalizer" 136 | version = "3.3.2" 137 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 138 | optional = false 139 | python-versions = ">=3.7.0" 140 | files = [ 141 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 142 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 143 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 144 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 145 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 146 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 147 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 148 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 149 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 150 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 151 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 152 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 153 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 154 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 155 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 156 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 157 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 158 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 159 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 160 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 161 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 162 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 163 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 164 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 165 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 166 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 167 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 168 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 169 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 170 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 171 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 172 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 173 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 174 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 175 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 176 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 177 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 178 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 179 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 180 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 181 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 182 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 183 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 184 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 185 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 186 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 187 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, 188 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, 189 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, 190 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, 191 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, 192 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, 193 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, 194 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, 195 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, 196 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, 197 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, 198 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, 199 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, 200 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 201 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 202 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 203 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 204 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 205 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 206 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 207 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 208 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 209 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 210 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 211 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 212 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 213 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 214 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 215 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 216 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 217 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 218 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 219 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 220 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 221 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 222 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 223 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 224 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 225 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 226 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 227 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 228 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 229 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 230 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 231 | ] 232 | 233 | [[package]] 234 | name = "click" 235 | version = "8.1.7" 236 | description = "Composable command line interface toolkit" 237 | optional = false 238 | python-versions = ">=3.7" 239 | files = [ 240 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 241 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 242 | ] 243 | 244 | [package.dependencies] 245 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 246 | 247 | [[package]] 248 | name = "colorama" 249 | version = "0.4.6" 250 | description = "Cross-platform colored terminal text." 251 | optional = false 252 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 253 | files = [ 254 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 255 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 256 | ] 257 | 258 | [[package]] 259 | name = "decorator" 260 | version = "5.1.1" 261 | description = "Decorators for Humans" 262 | optional = false 263 | python-versions = ">=3.5" 264 | files = [ 265 | {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, 266 | {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, 267 | ] 268 | 269 | [[package]] 270 | name = "django" 271 | version = "4.2.7" 272 | description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." 273 | optional = false 274 | python-versions = ">=3.8" 275 | files = [ 276 | {file = "Django-4.2.7-py3-none-any.whl", hash = "sha256:e1d37c51ad26186de355cbcec16613ebdabfa9689bbade9c538835205a8abbe9"}, 277 | {file = "Django-4.2.7.tar.gz", hash = "sha256:8e0f1c2c2786b5c0e39fe1afce24c926040fad47c8ea8ad30aaf1188df29fc41"}, 278 | ] 279 | 280 | [package.dependencies] 281 | asgiref = ">=3.6.0,<4" 282 | "backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} 283 | sqlparse = ">=0.3.1" 284 | tzdata = {version = "*", markers = "sys_platform == \"win32\""} 285 | 286 | [package.extras] 287 | argon2 = ["argon2-cffi (>=19.1.0)"] 288 | bcrypt = ["bcrypt"] 289 | 290 | [[package]] 291 | name = "executing" 292 | version = "2.0.1" 293 | description = "Get the currently executing AST node of a frame, and other information" 294 | optional = false 295 | python-versions = ">=3.5" 296 | files = [ 297 | {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, 298 | {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, 299 | ] 300 | 301 | [package.extras] 302 | tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] 303 | 304 | [[package]] 305 | name = "idna" 306 | version = "3.6" 307 | description = "Internationalized Domain Names in Applications (IDNA)" 308 | optional = false 309 | python-versions = ">=3.5" 310 | files = [ 311 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 312 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 313 | ] 314 | 315 | [[package]] 316 | name = "ipdb" 317 | version = "0.13.13" 318 | description = "IPython-enabled pdb" 319 | optional = false 320 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 321 | files = [ 322 | {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, 323 | {file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"}, 324 | ] 325 | 326 | [package.dependencies] 327 | decorator = {version = "*", markers = "python_version > \"3.6\""} 328 | ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\""} 329 | tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""} 330 | 331 | [[package]] 332 | name = "ipython" 333 | version = "8.12.3" 334 | description = "IPython: Productive Interactive Computing" 335 | optional = false 336 | python-versions = ">=3.8" 337 | files = [ 338 | {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, 339 | {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, 340 | ] 341 | 342 | [package.dependencies] 343 | appnope = {version = "*", markers = "sys_platform == \"darwin\""} 344 | backcall = "*" 345 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 346 | decorator = "*" 347 | jedi = ">=0.16" 348 | matplotlib-inline = "*" 349 | pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} 350 | pickleshare = "*" 351 | prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" 352 | pygments = ">=2.4.0" 353 | stack-data = "*" 354 | traitlets = ">=5" 355 | typing-extensions = {version = "*", markers = "python_version < \"3.10\""} 356 | 357 | [package.extras] 358 | 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"] 359 | black = ["black"] 360 | 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"] 361 | kernel = ["ipykernel"] 362 | nbconvert = ["nbconvert"] 363 | nbformat = ["nbformat"] 364 | notebook = ["ipywidgets", "notebook"] 365 | parallel = ["ipyparallel"] 366 | qtconsole = ["qtconsole"] 367 | test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] 368 | test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] 369 | 370 | [[package]] 371 | name = "isort" 372 | version = "5.12.0" 373 | description = "A Python utility / library to sort Python imports." 374 | optional = false 375 | python-versions = ">=3.8.0" 376 | files = [ 377 | {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, 378 | {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, 379 | ] 380 | 381 | [package.extras] 382 | colors = ["colorama (>=0.4.3)"] 383 | pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] 384 | plugins = ["setuptools"] 385 | requirements-deprecated-finder = ["pip-api", "pipreqs"] 386 | 387 | [[package]] 388 | name = "jedi" 389 | version = "0.19.1" 390 | description = "An autocompletion tool for Python that can be used for text editors." 391 | optional = false 392 | python-versions = ">=3.6" 393 | files = [ 394 | {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, 395 | {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, 396 | ] 397 | 398 | [package.dependencies] 399 | parso = ">=0.8.3,<0.9.0" 400 | 401 | [package.extras] 402 | 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)"] 403 | qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] 404 | testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] 405 | 406 | [[package]] 407 | name = "jinja2" 408 | version = "3.1.2" 409 | description = "A very fast and expressive template engine." 410 | optional = false 411 | python-versions = ">=3.7" 412 | files = [ 413 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 414 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 415 | ] 416 | 417 | [package.dependencies] 418 | MarkupSafe = ">=2.0" 419 | 420 | [package.extras] 421 | i18n = ["Babel (>=2.7)"] 422 | 423 | [[package]] 424 | name = "markupsafe" 425 | version = "2.1.3" 426 | description = "Safely add untrusted strings to HTML/XML markup." 427 | optional = false 428 | python-versions = ">=3.7" 429 | files = [ 430 | {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, 431 | {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, 432 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, 433 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, 434 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, 435 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, 436 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, 437 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, 438 | {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, 439 | {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, 440 | {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, 441 | {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, 442 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, 443 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, 444 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, 445 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, 446 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, 447 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, 448 | {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, 449 | {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, 450 | {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, 451 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, 452 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, 453 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, 454 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, 455 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, 456 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, 457 | {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, 458 | {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, 459 | {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, 460 | {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, 461 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, 462 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, 463 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, 464 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, 465 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, 466 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, 467 | {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, 468 | {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, 469 | {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, 470 | {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, 471 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, 472 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, 473 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, 474 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, 475 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, 476 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, 477 | {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, 478 | {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, 479 | {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, 480 | ] 481 | 482 | [[package]] 483 | name = "matplotlib-inline" 484 | version = "0.1.6" 485 | description = "Inline Matplotlib backend for Jupyter" 486 | optional = false 487 | python-versions = ">=3.5" 488 | files = [ 489 | {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, 490 | {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, 491 | ] 492 | 493 | [package.dependencies] 494 | traitlets = "*" 495 | 496 | [[package]] 497 | name = "mypy-extensions" 498 | version = "1.0.0" 499 | description = "Type system extensions for programs checked with the mypy type checker." 500 | optional = false 501 | python-versions = ">=3.5" 502 | files = [ 503 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 504 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 505 | ] 506 | 507 | [[package]] 508 | name = "parso" 509 | version = "0.8.3" 510 | description = "A Python Parser" 511 | optional = false 512 | python-versions = ">=3.6" 513 | files = [ 514 | {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, 515 | {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, 516 | ] 517 | 518 | [package.extras] 519 | qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] 520 | testing = ["docopt", "pytest (<6.0.0)"] 521 | 522 | [[package]] 523 | name = "pathspec" 524 | version = "0.11.2" 525 | description = "Utility library for gitignore style pattern matching of file paths." 526 | optional = false 527 | python-versions = ">=3.7" 528 | files = [ 529 | {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, 530 | {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, 531 | ] 532 | 533 | [[package]] 534 | name = "pexpect" 535 | version = "4.9.0" 536 | description = "Pexpect allows easy control of interactive console applications." 537 | optional = false 538 | python-versions = "*" 539 | files = [ 540 | {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, 541 | {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, 542 | ] 543 | 544 | [package.dependencies] 545 | ptyprocess = ">=0.5" 546 | 547 | [[package]] 548 | name = "pickleshare" 549 | version = "0.7.5" 550 | description = "Tiny 'shelve'-like database with concurrency support" 551 | optional = false 552 | python-versions = "*" 553 | files = [ 554 | {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, 555 | {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, 556 | ] 557 | 558 | [[package]] 559 | name = "platformdirs" 560 | version = "4.0.0" 561 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 562 | optional = false 563 | python-versions = ">=3.7" 564 | files = [ 565 | {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, 566 | {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, 567 | ] 568 | 569 | [package.extras] 570 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] 571 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] 572 | 573 | [[package]] 574 | name = "prompt-toolkit" 575 | version = "3.0.41" 576 | description = "Library for building powerful interactive command lines in Python" 577 | optional = false 578 | python-versions = ">=3.7.0" 579 | files = [ 580 | {file = "prompt_toolkit-3.0.41-py3-none-any.whl", hash = "sha256:f36fe301fafb7470e86aaf90f036eef600a3210be4decf461a5b1ca8403d3cb2"}, 581 | {file = "prompt_toolkit-3.0.41.tar.gz", hash = "sha256:941367d97fc815548822aa26c2a269fdc4eb21e9ec05fc5d447cf09bad5d75f0"}, 582 | ] 583 | 584 | [package.dependencies] 585 | wcwidth = "*" 586 | 587 | [[package]] 588 | name = "ptyprocess" 589 | version = "0.7.0" 590 | description = "Run a subprocess in a pseudo terminal" 591 | optional = false 592 | python-versions = "*" 593 | files = [ 594 | {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, 595 | {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, 596 | ] 597 | 598 | [[package]] 599 | name = "pure-eval" 600 | version = "0.2.2" 601 | description = "Safely evaluate AST nodes without side effects" 602 | optional = false 603 | python-versions = "*" 604 | files = [ 605 | {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, 606 | {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, 607 | ] 608 | 609 | [package.extras] 610 | tests = ["pytest"] 611 | 612 | [[package]] 613 | name = "pygments" 614 | version = "2.17.2" 615 | description = "Pygments is a syntax highlighting package written in Python." 616 | optional = false 617 | python-versions = ">=3.7" 618 | files = [ 619 | {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, 620 | {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, 621 | ] 622 | 623 | [package.extras] 624 | plugins = ["importlib-metadata"] 625 | windows-terminal = ["colorama (>=0.4.6)"] 626 | 627 | [[package]] 628 | name = "requests" 629 | version = "2.31.0" 630 | description = "Python HTTP for Humans." 631 | optional = false 632 | python-versions = ">=3.7" 633 | files = [ 634 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 635 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 636 | ] 637 | 638 | [package.dependencies] 639 | certifi = ">=2017.4.17" 640 | charset-normalizer = ">=2,<4" 641 | idna = ">=2.5,<4" 642 | urllib3 = ">=1.21.1,<3" 643 | 644 | [package.extras] 645 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 646 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 647 | 648 | [[package]] 649 | name = "six" 650 | version = "1.16.0" 651 | description = "Python 2 and 3 compatibility utilities" 652 | optional = false 653 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 654 | files = [ 655 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 656 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 657 | ] 658 | 659 | [[package]] 660 | name = "sqlparse" 661 | version = "0.4.4" 662 | description = "A non-validating SQL parser." 663 | optional = false 664 | python-versions = ">=3.5" 665 | files = [ 666 | {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, 667 | {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, 668 | ] 669 | 670 | [package.extras] 671 | dev = ["build", "flake8"] 672 | doc = ["sphinx"] 673 | test = ["pytest", "pytest-cov"] 674 | 675 | [[package]] 676 | name = "stack-data" 677 | version = "0.6.3" 678 | description = "Extract data from python stack frames and tracebacks for informative displays" 679 | optional = false 680 | python-versions = "*" 681 | files = [ 682 | {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, 683 | {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, 684 | ] 685 | 686 | [package.dependencies] 687 | asttokens = ">=2.1.0" 688 | executing = ">=1.2.0" 689 | pure-eval = "*" 690 | 691 | [package.extras] 692 | tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] 693 | 694 | [[package]] 695 | name = "tomli" 696 | version = "2.0.1" 697 | description = "A lil' TOML parser" 698 | optional = false 699 | python-versions = ">=3.7" 700 | files = [ 701 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 702 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 703 | ] 704 | 705 | [[package]] 706 | name = "traitlets" 707 | version = "5.14.0" 708 | description = "Traitlets Python configuration system" 709 | optional = false 710 | python-versions = ">=3.8" 711 | files = [ 712 | {file = "traitlets-5.14.0-py3-none-any.whl", hash = "sha256:f14949d23829023013c47df20b4a76ccd1a85effb786dc060f34de7948361b33"}, 713 | {file = "traitlets-5.14.0.tar.gz", hash = "sha256:fcdaa8ac49c04dfa0ed3ee3384ef6dfdb5d6f3741502be247279407679296772"}, 714 | ] 715 | 716 | [package.extras] 717 | docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] 718 | test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] 719 | 720 | [[package]] 721 | name = "typing-extensions" 722 | version = "4.8.0" 723 | description = "Backported and Experimental Type Hints for Python 3.8+" 724 | optional = false 725 | python-versions = ">=3.8" 726 | files = [ 727 | {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, 728 | {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, 729 | ] 730 | 731 | [[package]] 732 | name = "tzdata" 733 | version = "2023.3" 734 | description = "Provider of IANA time zone data" 735 | optional = false 736 | python-versions = ">=2" 737 | files = [ 738 | {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, 739 | {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, 740 | ] 741 | 742 | [[package]] 743 | name = "urllib3" 744 | version = "2.1.0" 745 | description = "HTTP library with thread-safe connection pooling, file post, and more." 746 | optional = false 747 | python-versions = ">=3.8" 748 | files = [ 749 | {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, 750 | {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, 751 | ] 752 | 753 | [package.extras] 754 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 755 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 756 | zstd = ["zstandard (>=0.18.0)"] 757 | 758 | [[package]] 759 | name = "wcwidth" 760 | version = "0.2.12" 761 | description = "Measures the displayed width of unicode strings in a terminal" 762 | optional = false 763 | python-versions = "*" 764 | files = [ 765 | {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"}, 766 | {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"}, 767 | ] 768 | 769 | [metadata] 770 | lock-version = "2.0" 771 | python-versions = "^3.8" 772 | content-hash = "88a507aabbb5bd02bec5671d8fa3a9190fef23b2eaa38b14f1fead68bbc89e0b" 773 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "django-importmap" 3 | version = "0.3.0" 4 | description = "JavaScript import maps for Django" 5 | authors = ["Dave Gaeddert "] 6 | license = "MIT" 7 | readme = "README.md" 8 | homepage = "https://github.com/dropseed/django-importmap" 9 | documentation = "https://github.com/dropseed/django-importmap" 10 | repository = "https://github.com/dropseed/django-importmap" 11 | keywords = ["django", "javascript", "import", "map", "import-maps"] 12 | packages = [ 13 | { include = "importmap" }, 14 | ] 15 | classifiers = [ 16 | "Environment :: Web Environment", 17 | "Framework :: Django", 18 | "Intended Audience :: Developers", 19 | "Operating System :: OS Independent", 20 | "Programming Language :: Python", 21 | "Programming Language :: Python :: 3", 22 | "Programming Language :: Python :: 3 :: Only", 23 | "Programming Language :: Python :: 3.8", 24 | "Programming Language :: Python :: 3.9", 25 | "Programming Language :: Python :: 3.10", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.8" 30 | tomli = {version = "*", markers = "python_version < '3.12'"} 31 | requests = "*" 32 | 33 | [tool.poetry.dev-dependencies] 34 | black = "^22.1.0" 35 | Django = "^4.0.0" 36 | ipdb = "^0.13.9" 37 | isort = "^5.10.1" 38 | jinja2 = "^3.1.2" 39 | 40 | [build-system] 41 | requires = ["poetry-core>=1.0.0"] 42 | build-backend = "poetry.core.masonry.api" 43 | -------------------------------------------------------------------------------- /scripts/format: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | ./.venv/bin/black importmap "$@" 3 | ./.venv/bin/black test_project "$@" 4 | ./.venv/bin/isort --profile=black importmap "$@" 5 | ./.venv/bin/isort --profile=black test_project "$@" 6 | -------------------------------------------------------------------------------- /scripts/install: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | POETRY_VIRTUALENVS_IN_PROJECT=true poetry install 3 | -------------------------------------------------------------------------------- /scripts/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | ./scripts/format --check 3 | ./scripts/test 4 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | cd test_project 3 | ../.venv/bin/python manage.py test "$@" 4 | -------------------------------------------------------------------------------- /test_project/app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AppConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "app" 7 | -------------------------------------------------------------------------------- /test_project/app/jinja2.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.templatetags.static import static 3 | from jinja2 import Environment 4 | 5 | from importmap import Importmap 6 | 7 | 8 | def environment(**options): 9 | env = Environment(**options) 10 | env.globals.update( 11 | { 12 | "importmap": Importmap.json( 13 | development=settings.DEBUG, extra_imports={"myjs": static("myjs.js")} 14 | ) 15 | } 16 | ) 17 | return env 18 | -------------------------------------------------------------------------------- /test_project/app/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropseed/django-importmap/64dd5c767dd650e263d09b75c15cd9221358f080/test_project/app/models.py -------------------------------------------------------------------------------- /test_project/app/static/app.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | console.log(React); 4 | -------------------------------------------------------------------------------- /test_project/app/templates/index.html: -------------------------------------------------------------------------------- 1 | {% load static importmap %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | {% static "myjs.js" as myjs_static %} 11 | {% importmap_scripts myjs=myjs_static %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test_project/app/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from django.views.generic import TemplateView 3 | 4 | urlpatterns = [ 5 | path("", TemplateView.as_view(template_name="index.html"), name="index"), 6 | path( 7 | "jinja", 8 | TemplateView.as_view(template_name="index_jinja.html"), 9 | name="index_jinja", 10 | ), 11 | ] 12 | -------------------------------------------------------------------------------- /test_project/importmap.lock: -------------------------------------------------------------------------------- 1 | { 2 | "config_hash": "7cf74163241b9a96851685de024eb665", 3 | "importmap": { 4 | "imports": { 5 | "react": "https://ga.jspm.io/npm:react@17.0.2/index.js", 6 | "react-dom": "https://ga.jspm.io/npm:react-dom@17.0.2/index.js" 7 | }, 8 | "scopes": { 9 | "https://ga.jspm.io/": { 10 | "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js", 11 | "scheduler": "https://ga.jspm.io/npm:scheduler@0.20.2/index.js" 12 | } 13 | } 14 | }, 15 | "importmap_dev": { 16 | "imports": { 17 | "react": "https://ga.jspm.io/npm:react@17.0.2/dev.index.js", 18 | "react-dom": "https://ga.jspm.io/npm:react-dom@17.0.2/dev.index.js" 19 | }, 20 | "scopes": { 21 | "https://ga.jspm.io/": { 22 | "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js", 23 | "scheduler": "https://ga.jspm.io/npm:scheduler@0.20.2/dev.index.js", 24 | "scheduler/tracing": "https://ga.jspm.io/npm:scheduler@0.20.2/dev.tracing.js" 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /test_project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /test_project/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.importmap.dependencies] 2 | react = "17.0.2" 3 | react-dom = "17.0.2" 4 | -------------------------------------------------------------------------------- /test_project/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for testproject project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = "test" 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | "django.contrib.admin", 35 | "django.contrib.auth", 36 | "django.contrib.contenttypes", 37 | "django.contrib.sessions", 38 | "django.contrib.messages", 39 | "django.contrib.staticfiles", 40 | "importmap", 41 | "app", 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | "django.middleware.security.SecurityMiddleware", 46 | "django.contrib.sessions.middleware.SessionMiddleware", 47 | "django.middleware.common.CommonMiddleware", 48 | "django.middleware.csrf.CsrfViewMiddleware", 49 | "django.contrib.auth.middleware.AuthenticationMiddleware", 50 | "django.contrib.messages.middleware.MessageMiddleware", 51 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 52 | ] 53 | 54 | ROOT_URLCONF = "app.urls" 55 | 56 | TEMPLATES = [ 57 | { 58 | "BACKEND": "django.template.backends.django.DjangoTemplates", 59 | "DIRS": [], 60 | "APP_DIRS": True, 61 | "OPTIONS": { 62 | "context_processors": [ 63 | "django.template.context_processors.debug", 64 | "django.template.context_processors.request", 65 | "django.contrib.auth.context_processors.auth", 66 | "django.contrib.messages.context_processors.messages", 67 | ], 68 | }, 69 | }, 70 | { 71 | "BACKEND": "django.template.backends.jinja2.Jinja2", 72 | "DIRS": [Path(__file__).resolve().parent / "templates" / "jinja2"], 73 | "APP_DIRS": False, 74 | "OPTIONS": { 75 | "environment": "app.jinja2.environment", 76 | }, 77 | }, 78 | ] 79 | 80 | WSGI_APPLICATION = "wsgi.application" 81 | 82 | 83 | # Database 84 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 85 | 86 | DATABASES = { 87 | "default": { 88 | "ENGINE": "django.db.backends.sqlite3", 89 | "NAME": BASE_DIR / "db.sqlite3", 90 | } 91 | } 92 | 93 | 94 | # Password validation 95 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 96 | 97 | AUTH_PASSWORD_VALIDATORS = [ 98 | { 99 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 100 | }, 101 | { 102 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 103 | }, 104 | { 105 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 106 | }, 107 | { 108 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 109 | }, 110 | ] 111 | 112 | 113 | # Internationalization 114 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 115 | 116 | LANGUAGE_CODE = "en-us" 117 | 118 | TIME_ZONE = "UTC" 119 | 120 | USE_I18N = True 121 | 122 | USE_L10N = True 123 | 124 | USE_TZ = True 125 | 126 | 127 | # Static files (CSS, JavaScript, Images) 128 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 129 | 130 | STATIC_URL = "/static/" 131 | 132 | # Default primary key field type 133 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 134 | 135 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 136 | -------------------------------------------------------------------------------- /test_project/templates/jinja2/index_jinja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test_project/tests.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.test import Client, TestCase, override_settings 3 | from django.urls import reverse 4 | 5 | 6 | class TestTemplate(TestCase): 7 | def setUp(self): 8 | self.client = Client() 9 | 10 | @override_settings(DEBUG=False) 11 | def test_template_output(self): 12 | url = reverse("index") 13 | response = self.client.get(url) 14 | 15 | self.assertEqual(response.status_code, 200) 16 | self.assertTemplateUsed(response, "index.html") 17 | self.assertContains(response, "es-module-shims.js") 18 | 19 | self.assertContains(response, "react@17.0.2/index.js") 20 | 21 | self.assertContains(response, "/static/myjs.js") 22 | 23 | @override_settings(DEBUG=True) 24 | def test_template_output_dev(self): 25 | url = reverse("index") 26 | response = self.client.get(url) 27 | # print(response.content.decode()) 28 | 29 | self.assertEqual(response.status_code, 200) 30 | self.assertTemplateUsed(response, "index.html") 31 | self.assertContains(response, "es-module-shims.js") 32 | 33 | self.assertContains(response, "react@17.0.2/dev.index.js") 34 | 35 | self.assertContains(response, "/static/myjs.js") 36 | 37 | @override_settings(DEBUG=False) 38 | def test_jinja_template_output(self): 39 | url = reverse("index_jinja") 40 | response = self.client.get(url) 41 | 42 | self.assertEqual(response.status_code, 200) 43 | self.assertContains(response, "es-module-shims.js") 44 | 45 | self.assertContains(response, "react@17.0.2/index.js") 46 | 47 | self.assertContains(response, "/static/myjs.js") 48 | 49 | @override_settings(DEBUG=True) 50 | def test_jinja_template_output_dev(self): 51 | url = reverse("index_jinja") 52 | response = self.client.get(url) 53 | 54 | self.assertEqual(response.status_code, 200) 55 | self.assertContains(response, "es-module-shims.js") 56 | 57 | self.assertContains(response, "react@17.0.2/dev.index.js") 58 | 59 | self.assertContains(response, "/static/myjs.js") 60 | -------------------------------------------------------------------------------- /test_project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") 15 | 16 | application = get_wsgi_application() 17 | --------------------------------------------------------------------------------