From 47bb4fbd233696c88ed576cdaf7890860eedbee2 Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Fri, 6 Feb 2026 14:33:56 -0600 Subject: [PATCH 01/34] worker: Use a volume to expose attack data to program image-based containers instead of creating new images for each attack + program pair. This is experimental and current breaks multi-worker setups, but in single-worker scenarios results in significant speedups: - ~4x speed improvement when the base program image has already been built. - ~20-30% speed improvment when the base program image must be built. This also adds some timing code around container setup / creation. --- docker-compose.yml | 4 +++- worker/worker.py | 27 ++++++++++++++++++--------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 82a0951..4867839 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: db: image: postgres volumes: - - db_data:/var/lib/postgresql/data + - db_data:/var/lib/postgresql networks: - backend environment: @@ -18,6 +18,7 @@ services: - backend volumes: - cctf:/cctf + - worktmp:/worktmp - /var/run/docker.sock:/var/run/docker.sock - ./pack:/pack depends_on: @@ -49,3 +50,4 @@ networks: volumes: cctf: db_data: + worktmp: diff --git a/worker/worker.py b/worker/worker.py index ce65c59..129f58d 100644 --- a/worker/worker.py +++ b/worker/worker.py @@ -74,20 +74,25 @@ def run(self) -> Result: base_docker_image, logs = client.images.build(path=self.source_dir, tag=self.get_repo_checksum()) logging.getLogger(__name__).info("built dockerfile: " + base_docker_image.id) + container_build_start = time.time() + # Build Docker image of the attack - with open(os.path.join(self.source_dir, "Dockerfile"), 'w') as f: - f.write(f"""FROM {docker_image_name} -WORKDIR /opt/dtanm -#COPY . . # Eventually we'll want to copy over environment files -ENTRYPOINT { os.path.join('/opt/dtanm', self.prog) } {self.args.decode()} -""") - docker_image, logs = client.images.build(path=self.source_dir) +# with open(os.path.join(self.source_dir, "Dockerfile"), 'w') as f: +# f.write(f"""FROM {docker_image_name} +#WORKDIR /opt/dtanm +##COPY . . # Eventually we'll want to copy over environment files +#ENTRYPOINT { os.path.join('/opt/dtanm', self.prog) } {self.args.decode()} +#""") + #docker_image, logs = client.images.build(path=self.source_dir) + + with open("/worktmp/args", 'w') as f: + f.write(f"{self.args.decode()}") with open(self.envs) as f: env_vars = [line.rstrip() for line in f.readlines()] # run Docker image of the attack - container = client.containers.create(image=docker_image.id, + container = client.containers.create(image=docker_image_name, #command=self.cmd, # The command is in the Dockerfile mem_limit=config.SCORING_MAX_MEMORY, pids_limit=config.SCORING_MAX_PROCESSES, @@ -95,7 +100,11 @@ def run(self) -> Result: cpu_period=10000, cpu_quota=int(10000 * config.SCORING_MAX_CPUS), detach=True, - network_disabled=getattr(config, "SCORING_DISABLE_NETWORK", True)) + network_disabled=getattr(config, "SCORING_DISABLE_NETWORK", True), + mounts=[docker.types.Mount(type="volume", target="/opt/dtanm/env", source="worktmp")]) + + container_build_elapsed = time.time() - container_build_start + logging.getLogger(__name__).info(f"{container_build_elapsed}") start_time = time.time() container.start() From dc1ed85ff0ee7118dd427130144b6cb4a6a88761 Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Tue, 10 Feb 2026 13:20:17 -0600 Subject: [PATCH 02/34] pipenv devs hate him --- worker/Pipfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/worker/Pipfile.lock b/worker/Pipfile.lock index 4181a6c..cda790c 100644 --- a/worker/Pipfile.lock +++ b/worker/Pipfile.lock @@ -32,11 +32,11 @@ }, "docker": { "hashes": [ - "sha256:03a46400c4080cb6f7aa997f881ddd84fef855499ece219d75fbdb53289c17ab", - "sha256:26eebadce7e298f55b76a88c4f8802476c5eaddbdbe38dbc6cce8781c47c9b54" + "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", + "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0" ], "index": "pypi", - "version": "==4.2.2" + "version": "==7.1.0" }, "dulwich": { "hashes": [ From d5b3c397f881abdde9a565343d69a6ed58d7b25d Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Tue, 10 Feb 2026 13:43:17 -0600 Subject: [PATCH 03/34] worker: Part 2 of using docker volumes to mount attack information into grading containers. The workers now create a worker-specific temporary subdirectory in the worktmp volume to store their info inside of. This temp subdir is mounted into the grading container using docker subvolumes instead of directly mounting the worktmp volume. This requires some jank atm because the python docker sdk doesn't support docker volumes in its current release (`main` does have support, however). I ended up manually poking at the 'VolumeOptions' entry in the docker.types.Mount type to manually add the Subpath option. afaict this works fine and is basically what py docker sdk update for subvolumes does. --- worker/worker.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/worker/worker.py b/worker/worker.py index 129f58d..135bf3c 100644 --- a/worker/worker.py +++ b/worker/worker.py @@ -41,6 +41,10 @@ def __enter__(self): self.working_dir = os.path.join(self.exercise_dir, "env") self.source_dir = os.path.join(self.exercise_dir, "src") + self.worktmp_subdir = os.uname()[1] # device hostname + self.worktmp_dir = os.path.join('/worktmp', self.worktmp_subdir) + os.mkdir(self.worktmp_dir) + if self.files_dir: shutil.copytree(self.files_dir, self.working_dir) else: @@ -54,6 +58,7 @@ def __enter__(self): def __exit__(self, *args): shutil.rmtree(self.exercise_dir) + shutil.rmtree(self.worktmp_dir) def get_repo_checksum(self) -> Optional[str]: if self.repo: @@ -70,7 +75,10 @@ def run(self) -> Result: try: base_docker_image = client.images.get(docker_image_name) except docker.errors.ImageNotFound as e: - shutil.copyfile('/pack/Dockerfile.build', os.path.join(self.source_dir, "Dockerfile")) + dockerfile_path = os.path.join(self.source_dir, "Dockerfile") + shutil.copyfile('/pack/Dockerfile.build', dockerfile_path) + with open(dockerfile_path, 'a') as f: + f.wirte(f"ENTRYPOINT { os.path.join('/opt/dtanm', self.prog) } $(cat /opt/dtanm/env/args)") base_docker_image, logs = client.images.build(path=self.source_dir, tag=self.get_repo_checksum()) logging.getLogger(__name__).info("built dockerfile: " + base_docker_image.id) @@ -85,12 +93,22 @@ def run(self) -> Result: #""") #docker_image, logs = client.images.build(path=self.source_dir) - with open("/worktmp/args", 'w') as f: + + + with open(os.path.join(self.worktmp_dir, 'args'), 'w') as f: f.write(f"{self.args.decode()}") with open(self.envs) as f: env_vars = [line.rstrip() for line in f.readlines()] + # Setup worktmp mount. + # The current version of the python docker sdk doesn't support subvolumes so + # we need to manually add the Subpath option to VolumeOptions. + # I think this is considered an implementation detail but it should be fine if + # we don't touch anything... + worktmp_mount = docker.types.Mount(type="volume", target="/opt/dtanm/env", source="dtanm_worktmp") + worktmp_mount['VolumeOptions'] = { 'Subpath': self.worktmp_subdir } # ewww + # run Docker image of the attack container = client.containers.create(image=docker_image_name, #command=self.cmd, # The command is in the Dockerfile @@ -101,10 +119,10 @@ def run(self) -> Result: cpu_quota=int(10000 * config.SCORING_MAX_CPUS), detach=True, network_disabled=getattr(config, "SCORING_DISABLE_NETWORK", True), - mounts=[docker.types.Mount(type="volume", target="/opt/dtanm/env", source="worktmp")]) + mounts=[worktmp_mount]) container_build_elapsed = time.time() - container_build_start - logging.getLogger(__name__).info(f"{container_build_elapsed}") + logging.info(f"{container_build_elapsed}") start_time = time.time() container.start() @@ -112,6 +130,7 @@ def run(self) -> Result: try: results = container.wait(timeout=config.SCORING_MAX_TIME) elapsed_time = time.time() - start_time # Originally time.perf_counter was used here. Perhaps that would be a better option in the future? + logging.info(elapsed_time) return_code = results['StatusCode'] except (requests.exceptions.ReadTimeout, requests.exceptions.ConnectionError): # They timed out. try: From e9a537ce310a78faa61e38cf43a889ca624f86df Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Tue, 17 Feb 2026 11:41:46 -0600 Subject: [PATCH 04/34] worker: Update all deps to their latest version. --- worker/Dockerfile | 4 +- worker/Pipfile | 2 +- worker/Pipfile.lock | 954 ++++++++++++++++++++++++++++++-------------- 3 files changed, 661 insertions(+), 299 deletions(-) diff --git a/worker/Dockerfile b/worker/Dockerfile index b6c24e2..3d37d84 100644 --- a/worker/Dockerfile +++ b/worker/Dockerfile @@ -1,11 +1,13 @@ -FROM python:3.8.3 +FROM python:3.14 RUN pip install pipenv WORKDIR /server COPY Pipfile Pipfile.lock /server/ +#COPY Pipfile /server/ RUN pipenv install --deploy +#RUN pipenv lock COPY . /server/ diff --git a/worker/Pipfile b/worker/Pipfile index 0ac772c..d1f553f 100644 --- a/worker/Pipfile +++ b/worker/Pipfile @@ -21,4 +21,4 @@ ipython = "*" mock = "*" [requires] -python_version = "3.8" +python_version = "3.14" diff --git a/worker/Pipfile.lock b/worker/Pipfile.lock index cda790c..46eec49 100644 --- a/worker/Pipfile.lock +++ b/worker/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "3a0d60836e1681f485f085f6c28858105c71708085a76db4f41bea1c6a2fef5c" + "sha256": "7bbed8859ffe551d6b845b71f0dcb84866d8a6eb20efc7912ec4c788ba1d8676" }, "pipfile-spec": 6, "requires": { - "python_version": "3.8" + "python_version": "3.14" }, "sources": [ { @@ -18,17 +18,130 @@ "default": { "certifi": { "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" - ], - "version": "==2020.6.20" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" + "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", + "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120" + ], + "markers": "python_version >= '3.7'", + "version": "==2026.1.4" + }, + "charset-normalizer": { + "hashes": [ + "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", + "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", + "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", + "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", + "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", + "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", + "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63", + "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", + "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", + "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", + "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", + "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", + "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", + "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af", + "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", + "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", + "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", + "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", + "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", + "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", + "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576", + "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", + "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", + "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", + "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", + "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", + "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", + "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", + "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", + "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", + "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", + "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", + "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a", + "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", + "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", + "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", + "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", + "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", + "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7", + "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", + "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", + "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", + "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", + "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", + "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", + "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2", + "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", + "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", + "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", + "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", + "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", + "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", + "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", + "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", + "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa", + "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", + "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", + "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", + "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", + "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", + "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", + "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", + "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", + "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", + "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", + "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", + "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", + "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", + "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", + "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", + "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3", + "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", + "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", + "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", + "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", + "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", + "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", + "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf", + "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", + "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", + "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac", + "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", + "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", + "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", + "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", + "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", + "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", + "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4", + "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84", + "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", + "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", + "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", + "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", + "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", + "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", + "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", + "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", + "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", + "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074", + "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3", + "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", + "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", + "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", + "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d", + "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", + "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", + "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", + "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", + "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", + "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", + "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", + "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", + "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.4" }, "docker": { "hashes": [ @@ -36,455 +149,702 @@ "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==7.1.0" }, "dulwich": { "hashes": [ - "sha256:036fe7f38a31185033fff6d6ecf13d294d4ed58de6cb8655f933732fa1780cde", - "sha256:17f03a08725b067ebc270ba4e57eb3df68ee77ece4bf930318a71328a6dd240f", - "sha256:2a025eb8a794c07ec3bc2f9659a50b4f60a25437a4dad7d06f5317f0390d2e8c", - "sha256:30355d696d93a58602349b546be6d993e9eee8b4e3d18fe4b4b523fc50e249dc", - "sha256:31a528b7b4274d4610aed5b6a3403b8250d4dbdf02aaef51cbb795c6d1ce6dda", - "sha256:372d1fd746b9513629f72e4e6a2a4c95bbd9c8fd904cf8ea83fe836c6e3b82be", - "sha256:3b6418af63e7b89c83e5d3df37643096f3afd35c0e5c988b3e2f8d55ba0cb89d", - "sha256:863bfb141a1cde6f9b235cac282d6b4c411d547caee6a414846b75db6e990e79", - "sha256:98484ede022da663c96b54bc8dcdb4407072cb50efd5d20d58ca4e7779931305", - "sha256:b332249d29918c0fa70d18d0461e7247ad5526a850ef3a1f166508a3af59c6f6", - "sha256:b45a6beead9d333a6e96a626b3762d12ddebe900973aeac1f6605c43bb29f723", - "sha256:cc46fea2fc3fc5b3bf473982bb3948d08b2d98a7fa59f28410d58d0c8ecf32e6", - "sha256:e4154eb65ecca0d7a7732797aabb63c9fd0f0279b4f8d5a92c7e1b981778138d", - "sha256:ef3f1057422a366443bc1a9621ed199a289b56d2958407716ac770ac0eb60716" + "sha256:06514b02da1e32a077062924d2c3b20a7bc76ab9b92eeac691f72b76b14111bc", + "sha256:1213da9832621b07dfaafdb651b74edb8966481475c52be0bff8dee352d75853", + "sha256:1a6583499b915fe5a8ac5595325f1e6a6a5a456de1575e0293e8a6ebb6915f3f", + "sha256:1bd4c872b617edc20e0b726479ec25705a0107a4bd16f83cb6cd108d86950634", + "sha256:1d95663441c930631d9d1765dc4f427dcc0662af45f42a0831357e60055ddb84", + "sha256:221be803b71b060c928e9faae4ab3e259ff5beac6e0c251ba3c176b51b5c2ffb", + "sha256:276ff18ae734fe4a1be66d4267216a51d2deab0ac981d722db3d32fcc2ac4ff8", + "sha256:2881fe077792474cbd8cae140e7c2c2263fc07017ba37051946d869151f79624", + "sha256:2ba6072e922b815006b036d0df59b25eb2523b89581fcf9f47feb43324629ba4", + "sha256:3051007bc2792b5a72fee938842cf45b66924d6d5147d824f3e609eb75fc0322", + "sha256:32b6fb1205b1d9c0e43986f9e4e5e50a3670014440e61498eca2b8ab6b00129f", + "sha256:3d07104735525f22bfec35514ac611cf328c89b7acb059316a4f6e583c8f09bc", + "sha256:3f090da8af238d7d9dba53a87b86a9e3e21529690b7536c253b1d5061bdb3a4d", + "sha256:581330cf799577f194fda2b5384b7ba50e095de7ff088779c027a6de63642de2", + "sha256:5cf6e9b5620a3e842663b58ad534da29944db6a6016ba61fc9bbed24830cd85f", + "sha256:6736abc2ce2994e38a00a3a4c80237b2b944e7c6f4e346119debdd2592312d83", + "sha256:78542a62fabea894943a1d01c9c477a56eee5f7d58d3bdee42c7e0622ddf6893", + "sha256:86271e17d76a667abb1d68dad83b6324422a1ab20d60be30395fd60a37b735b1", + "sha256:af39c560b75f64ec2d44519baf51ed1871be69e7ed4b0a4314215fa1ffe71195", + "sha256:c2f2b2692524468bcd91bf79aa1f420bbbb59996506ec434ad6e72227f52365b", + "sha256:cc0ab4ba7fd8617bebe20294dedaa8f713d1767ce059bfbefd971b911b702726", + "sha256:d1c33f6456e4335dfe6f4d3917fa7d77050d6470bbbaf8054b5c5084ee8e8cd1", + "sha256:e370e3cdd0b00c059ebee8371cc1644aa61d6de3de0ca5c2f2a5f075bf4c53d9", + "sha256:f90b54faeb995607c876cdb2c082c0f0af702e1ccb524c6126ce99a36536fa3f", + "sha256:fc791771859258ffbe34a6579ece30684a535ae0341fd2f5adfa5f64cc885c13", + "sha256:ff94f47f0b5787d4e6a0105daf51ff9cdb4e5b9d4e9f8dd01b58ba9a5b79bbd9" ], "index": "pypi", - "version": "==0.20.5" + "markers": "python_version >= '3.10'", + "version": "==1.0.0" + }, + "greenlet": { + "hashes": [ + "sha256:02925a0bfffc41e542c70aa14c7eda3593e4d7e274bfcccca1827e6c0875902e", + "sha256:04bee4775f40ecefcdaa9d115ab44736cd4b9c5fba733575bfe9379419582e13", + "sha256:070472cd156f0656f86f92e954591644e158fd65aa415ffbe2d44ca77656a8f5", + "sha256:09f51496a0bfbaa9d74d36a52d2580d1ef5ed4fdfcff0a73730abfbbbe1403dd", + "sha256:1108b61b06b5224656121c3c8ee8876161c491cbe74e5c519e0634c837cf93d5", + "sha256:12184c61e5d64268a160226fb4818af4df02cfead8379d7f8b99a56c3a54ff3e", + "sha256:14194f5f4305800ff329cbf02c5fcc88f01886cadd29941b807668a45f0d2336", + "sha256:20fedaadd422fa02695f82093f9a98bad3dab5fcda793c658b945fcde2ab27ba", + "sha256:27289986f4e5b0edec7b5a91063c109f0276abb09a7e9bdab08437525977c946", + "sha256:2f080e028001c5273e0b42690eaf359aeef9cb1389da0f171ea51a5dc3c7608d", + "sha256:301860987846c24cb8964bdec0e31a96ad4a2a801b41b4ef40963c1b44f33451", + "sha256:32e4ca9777c5addcbf42ff3915d99030d8e00173a56f80001fb3875998fe410b", + "sha256:33a956fe78bbbda82bfc95e128d61129b32d66bcf0a20a1f0c08aa4839ffa951", + "sha256:34a729e2e4e4ffe9ae2408d5ecaf12f944853f40ad724929b7585bca808a9d6f", + "sha256:39eda9ba259cc9801da05351eaa8576e9aa83eb9411e8f0c299e05d712a210f2", + "sha256:3a300354f27dd86bae5fbf7002e6dd2b3255cd372e9242c933faf5e859b703fe", + "sha256:3e0f3878ca3a3ff63ab4ea478585942b53df66ddde327b59ecb191b19dbbd62d", + "sha256:3e63252943c921b90abb035ebe9de832c436401d9c45f262d80e2d06cc659242", + "sha256:41848f3230b58c08bb43dee542e74a2a2e34d3c59dc3076cec9151aeeedcae98", + "sha256:49f4ad195d45f4a66a0eb9c1ba4832bb380570d361912fa3554746830d332149", + "sha256:4b065d3284be43728dd280f6f9a13990b56470b81be20375a207cdc814a983f2", + "sha256:4b9721549a95db96689458a1e0ae32412ca18776ed004463df3a9299c1b257ab", + "sha256:50e1457f4fed12a50e427988a07f0f9df53cf0ee8da23fab16e6732c2ec909d4", + "sha256:59913f1e5ada20fde795ba906916aea25d442abcc0593fba7e26c92b7ad76249", + "sha256:5fd23b9bc6d37b563211c6abbb1b3cab27db385a4449af5c32e932f93017080c", + "sha256:6423481193bbbe871313de5fd06a082f2649e7ce6e08015d2a76c1e9186ca5b3", + "sha256:65be2f026ca6a176f88fb935ee23c18333ccea97048076aef4db1ef5bc0713ac", + "sha256:67ea3fc73c8cd92f42467a72b75e8f05ed51a0e9b1d15398c913416f2dafd49f", + "sha256:71c767cf281a80d02b6c1bdc41c9468e1f5a494fb11bc8688c360524e273d7b1", + "sha256:76e39058e68eb125de10c92524573924e827927df5d3891fbc97bd55764a8774", + "sha256:7932f5f57609b6a3b82cc11877709aa7a98e3308983ed93552a1c377069b20c8", + "sha256:7a3ae05b3d225b4155bda56b072ceb09d05e974bc74be6c3fc15463cf69f33fd", + "sha256:7ab327905cabb0622adca5971e488064e35115430cec2c35a50fd36e72a315b3", + "sha256:7b2fe4150a0cf59f847a67db8c155ac36aed89080a6a639e9f16df5d6c6096f1", + "sha256:7e806ca53acf6d15a888405880766ec84721aa4181261cd11a457dfe9a7a4975", + "sha256:80aa4d79eb5564f2e0a6144fcc744b5a37c56c4a92d60920720e99210d88db0f", + "sha256:92497c78adf3ac703b57f1e3813c2d874f27f71a178f9ea5887855da413cd6d2", + "sha256:96aff77af063b607f2489473484e39a0bbae730f2ea90c9e5606c9b73c44174a", + "sha256:aec9ab04e82918e623415947921dea15851b152b822661cce3f8e4393c3df683", + "sha256:b066e8b50e28b503f604fa538adc764a638b38cf8e81e025011d26e8a627fa79", + "sha256:b31c05dd84ef6871dd47120386aed35323c944d86c3d91a17c4b8d23df62f15b", + "sha256:bd59acd8529b372775cd0fcbc5f420ae20681c5b045ce25bd453ed8455ab99b5", + "sha256:bfb2d1763d777de5ee495c85309460f6fd8146e50ec9d0ae0183dbf6f0a829d1", + "sha256:c620051669fd04ac6b60ebc70478210119c56e2d5d5df848baec4312e260e4ca", + "sha256:c9f9d5e7a9310b7a2f416dd13d2e3fd8b42d803968ea580b7c0f322ccb389b97", + "sha256:cb0feb07fe6e6a74615ee62a880007d976cf739b6669cce95daa7373d4fc69c5", + "sha256:cc98b9c4e4870fa983436afa999d4eb16b12872fab7071423d5262fa7120d57a", + "sha256:d842c94b9155f1c9b3058036c24ffb8ff78b428414a19792b2380be9cecf4f36", + "sha256:da19609432f353fed186cc1b85e9440db93d489f198b4bdf42ae19cc9d9ac9b4", + "sha256:e0093bd1a06d899892427217f0ff2a3c8f306182b8c754336d32e2d587c131b4", + "sha256:e2e7e882f83149f0a71ac822ebf156d902e7a5d22c9045e3e0d1daf59cee2cc9", + "sha256:e84b51cbebf9ae573b5fbd15df88887815e3253fc000a7d0ff95170e8f7e9729", + "sha256:ed6b402bc74d6557a705e197d47f9063733091ed6357b3de33619d8a8d93ac53" + ], + "markers": "python_version >= '3.10'", + "version": "==3.3.1" }, "idna": { "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", + "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" ], - "version": "==2.10" + "markers": "python_version >= '3.8'", + "version": "==3.11" }, "psycopg2": { "hashes": [ - "sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535", - "sha256:2327bf42c1744a434ed8ed0bbaa9168cac7ee5a22a9001f6fc85c33b8a4a14b7", - "sha256:27c633f2d5db0fc27b51f1b08f410715b59fa3802987aec91aeb8f562724e95c", - "sha256:2c0afb40cfb4d53487ee2ebe128649028c9a78d2476d14a67781e45dc287f080", - "sha256:2df2bf1b87305bd95eb3ac666ee1f00a9c83d10927b8144e8e39644218f4cf81", - "sha256:440a3ea2c955e89321a138eb7582aa1d22fe286c7d65e26a2c5411af0a88ae72", - "sha256:6a471d4d2a6f14c97a882e8d3124869bc623f3df6177eefe02994ea41fd45b52", - "sha256:6b306dae53ec7f4f67a10942cf8ac85de930ea90e9903e2df4001f69b7833f7e", - "sha256:a0984ff49e176062fcdc8a5a2a670c9bb1704a2f69548bce8f8a7bad41c661bf", - "sha256:ac5b23d0199c012ad91ed1bbb971b7666da651c6371529b1be8cbe2a7bf3c3a9", - "sha256:acf56d564e443e3dea152efe972b1434058244298a94348fc518d6dd6a9fb0bb", - "sha256:d3b29d717d39d3580efd760a9a46a7418408acebbb784717c90d708c9ed5f055", - "sha256:f7d46240f7a1ae1dd95aab38bd74f7428d46531f69219954266d669da60c0818" + "sha256:103e857f46bb76908768ead4e2d0ba1d1a130e7b8ed77d3ae91e8b33481813e8", + "sha256:210daed32e18f35e3140a1ebe059ac29209dd96468f2f7559aa59f75ee82a5cb", + "sha256:6ecddcf573777536bddfefaea8079ce959287798c8f5804bee6933635d538924", + "sha256:8dc379166b5b7d5ea66dcebf433011dfc51a7bb8a5fc12367fa05668e5fc53c8", + "sha256:964d31caf728e217c697ff77ea69c2ba0865fa41ec20bb00f0977e62fdcc52e3", + "sha256:e03e4a6dbe87ff81540b434f2e5dc2bddad10296db5eea7bdc995bf5f4162938", + "sha256:f10a48acba5fe6e312b891f290b4d2ca595fc9a06850fe53320beac353575578" ], "index": "pypi", - "version": "==2.8.5" + "markers": "python_version >= '3.9'", + "version": "==2.9.11" }, "redis": { "hashes": [ - "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", - "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" + "sha256:01f591f8598e483f1842d429e8ae3a820804566f1c73dca1b80e23af9fba0497", + "sha256:4dd5bf4bd4ae80510267f14185a15cba2a38666b941aff68cccf0256b51c1f26" ], "index": "pypi", - "version": "==3.5.3" + "markers": "python_version >= '3.10'", + "version": "==7.2.0" }, "requests": { "hashes": [ - "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", - "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", + "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf" ], - "version": "==2.24.0" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "version": "==1.15.0" + "markers": "python_version >= '3.9'", + "version": "==2.32.5" }, "sqlalchemy": { "hashes": [ - "sha256:0942a3a0df3f6131580eddd26d99071b48cfe5aaf3eab2783076fbc5a1c1882e", - "sha256:0ec575db1b54909750332c2e335c2bb11257883914a03bc5a3306a4488ecc772", - "sha256:109581ccc8915001e8037b73c29590e78ce74be49ca0a3630a23831f9e3ed6c7", - "sha256:16593fd748944726540cd20f7e83afec816c2ac96b082e26ae226e8f7e9688cf", - "sha256:427273b08efc16a85aa2b39892817e78e3ed074fcb89b2a51c4979bae7e7ba98", - "sha256:50c4ee32f0e1581828843267d8de35c3298e86ceecd5e9017dc45788be70a864", - "sha256:512a85c3c8c3995cc91af3e90f38f460da5d3cade8dc3a229c8e0879037547c9", - "sha256:57aa843b783179ab72e863512e14bdcba186641daf69e4e3a5761d705dcc35b1", - "sha256:621f58cd921cd71ba6215c42954ffaa8a918eecd8c535d97befa1a8acad986dd", - "sha256:6ac2558631a81b85e7fb7a44e5035347938b0a73f5fdc27a8566777d0792a6a4", - "sha256:716754d0b5490bdcf68e1e4925edc02ac07209883314ad01a137642ddb2056f1", - "sha256:736d41cfebedecc6f159fc4ac0769dc89528a989471dc1d378ba07d29a60ba1c", - "sha256:8619b86cb68b185a778635be5b3e6018623c0761dde4df2f112896424aa27bd8", - "sha256:87fad64529cde4f1914a5b9c383628e1a8f9e3930304c09cf22c2ae118a1280e", - "sha256:89494df7f93b1836cae210c42864b292f9b31eeabca4810193761990dc689cce", - "sha256:8cac7bb373a5f1423e28de3fd5fc8063b9c8ffe8957dc1b1a59cb90453db6da1", - "sha256:8fd452dc3d49b3cc54483e033de6c006c304432e6f84b74d7b2c68afa2569ae5", - "sha256:adad60eea2c4c2a1875eb6305a0b6e61a83163f8e233586a4d6a55221ef984fe", - "sha256:c26f95e7609b821b5f08a72dab929baa0d685406b953efd7c89423a511d5c413", - "sha256:cbe1324ef52ff26ccde2cb84b8593c8bf930069dfc06c1e616f1bfd4e47f48a3", - "sha256:d05c4adae06bd0c7f696ae3ec8d993ed8ffcc4e11a76b1b35a5af8a099bd2284", - "sha256:d98bc827a1293ae767c8f2f18be3bb5151fd37ddcd7da2a5f9581baeeb7a3fa1", - "sha256:da2fb75f64792c1fc64c82313a00c728a7c301efe6a60b7a9fe35b16b4368ce7", - "sha256:e4624d7edb2576cd72bb83636cd71c8ce544d8e272f308bd80885056972ca299", - "sha256:e89e0d9e106f8a9180a4ca92a6adde60c58b1b0299e1b43bd5e0312f535fbf33", - "sha256:f11c2437fb5f812d020932119ba02d9e2bc29a6eca01a055233a8b449e3e1e7d", - "sha256:f57be5673e12763dd400fea568608700a63ce1c6bd5bdbc3cc3a2c5fdb045274", - "sha256:fc728ece3d5c772c196fd338a99798e7efac7a04f9cb6416299a3638ee9a94cd" + "sha256:09168817d6c19954d3b7655da6ba87fcb3a62bb575fb396a81a8b6a9fadfe8b5", + "sha256:0cc3117db526cad3e61074100bd2867b533e2c7dc1569e95c14089735d6fb4fe", + "sha256:181903fe8c1b9082995325f1b2e84ac078b1189e2819380c2303a5f90e114a62", + "sha256:1bc3f601f0a818d27bfe139f6766487d9c88502062a2cd3a7ee6c342e81d5047", + "sha256:1e6199143d51e3e1168bedd98cc698397404a8f7508831b81b6a29b18b051069", + "sha256:2347c3f0efc4de367ba00218e0ae5c4ba2306e47216ef80d6e31761ac97cb0b9", + "sha256:261c4b1f101b4a411154f1da2b76497d73abbfc42740029205d4d01fa1052684", + "sha256:33e462154edb9493f6c3ad2125931e273bbd0be8ae53f3ecd1c161ea9a1dd366", + "sha256:37fee2164cf21417478b6a906adc1a91d69ae9aba8f9533e67ce882f4bb1de53", + "sha256:3a9a72b0da8387f15d5810f1facca8f879de9b85af8c645138cba61ea147968c", + "sha256:3aac08f7546179889c62b53b18ebf1148b10244b3405569c93984b0388d016a7", + "sha256:3c32e993bc57be6d177f7d5d31edb93f30726d798ad86ff9066d75d9bf2e0b6b", + "sha256:412f26bb4ba942d52016edc8d12fb15d91d3cd46b0047ba46e424213ad407bcb", + "sha256:42a1643dc5427b69aca967dae540a90b0fbf57eaf248f13a90ea5930e0966863", + "sha256:4396c948d8217e83e2c202fbdcc0389cf8c93d2c1c5e60fa5c5a955eae0e64be", + "sha256:4dafb537740eef640c4d6a7c254611dca2df87eaf6d14d6a5fca9d1f4c3fc0fa", + "sha256:4f52f7291a92381e9b4de9050b0a65ce5d6a763333406861e33906b8aa4906bf", + "sha256:52fe29b3817bd191cc20bad564237c808967972c97fa683c04b28ec8979ae36f", + "sha256:56bdd261bfd0895452006d5316cbf35739c53b9bb71a170a331fa0ea560b2ada", + "sha256:585af6afe518732d9ccd3aea33af2edaae4a7aa881af5d8f6f4fe3a368699597", + "sha256:590be24e20e2424a4c3c1b0835e9405fa3d0af5823a1a9fc02e5dff56471515f", + "sha256:64901e08c33462acc9ec3bad27fc7a5c2b6491665f2aa57564e57a4f5d7c52ad", + "sha256:6ac245604295b521de49b465bab845e3afe6916bcb2147e5929c8041b4ec0545", + "sha256:6f827fd687fa1ba7f51699e1132129eac8db8003695513fcf13fc587e1bd47a5", + "sha256:70ed2830b169a9960193f4d4322d22be5c0925357d82cbf485b3369893350908", + "sha256:716be5bcabf327b6d5d265dbdc6213a01199be587224eb991ad0d37e83d728fd", + "sha256:7568fe771f974abadce52669ef3a03150ff03186d8eb82613bc8adc435a03f01", + "sha256:77f8071d8fbcbb2dd11b7fd40dedd04e8ebe2eb80497916efedba844298065ef", + "sha256:82ec52100ec1e6ec671563bbd02d7c7c8d0b9e71a0723c72f22ecf52d1755330", + "sha256:895296687ad06dc9b11a024cf68e8d9d3943aa0b4964278d2553b86f1b267735", + "sha256:8d3b44b3d0ab2f1319d71d9863d76eeb46766f8cf9e921ac293511804d39813f", + "sha256:8d679b5f318423eacb61f933a9a0f75535bfca7056daeadbf6bd5bcee6183aee", + "sha256:8e84b09a9b0f19accedcbeff5c2caf36e0dd537341a33aad8d680336152dc34e", + "sha256:9094c8b3197db12aa6f05c51c05daaad0a92b8c9af5388569847b03b1007fb1b", + "sha256:90bde6c6b1827565a95fde597da001212ab436f1b2e0c2dcc7246e14db26e2a3", + "sha256:9397b381dcee8a2d6b99447ae85ea2530dcac82ca494d1db877087a13e38926d", + "sha256:93a12da97cca70cea10d4b4fc602589c4511f96c1f8f6c11817620c021d21d00", + "sha256:93bb0aae40b52c57fd74ef9c6933c08c040ba98daf23ad33c3f9893494b8d3ce", + "sha256:94b1e5f3a5f1ff4f42d5daab047428cd45a3380e51e191360a35cef71c9a7a2a", + "sha256:965c62be8256d10c11f8907e7a8d3e18127a4c527a5919d85fa87fd9ecc2cfdc", + "sha256:96c7cca1a4babaaf3bfff3e4e606e38578856917e52f0384635a95b226c87764", + "sha256:9bcdce05f056622a632f1d44bb47dbdb677f58cad393612280406ce37530eb6d", + "sha256:9d80ea2ac519c364a7286e8d765d6cd08648f5b21ca855a8017d9871f075542d", + "sha256:a1e8cc6cc01da346dc92d9509a63033b9b1bda4fed7a7a7807ed385c7dccdc10", + "sha256:ab65cb2885a9f80f979b85aa4e9c9165a31381ca322cbde7c638fe6eefd1ec39", + "sha256:af865c18752d416798dae13f83f38927c52f085c52e2f32b8ab0fef46fdd02c2", + "sha256:b1e14b2f6965a685c7128bd315e27387205429c2e339eeec55cb75ca4ab0ea2e", + "sha256:b2a9f9aee38039cf4755891a1e50e1effcc42ea6ba053743f452c372c3152b1b", + "sha256:be6c0466b4c25b44c5d82b0426b5501de3c424d7a3220e86cd32f319ba56798e", + "sha256:c4e2cc868b7b5208aec6c960950b7bb821f82c2fe66446c92ee0a571765e91a5", + "sha256:c805fa6e5d461329fa02f53f88c914d189ea771b6821083937e79550bf31fc19", + "sha256:cf36851ee7219c170bb0793dbc3da3e80c582e04a5437bc601bfe8c85c9216d7", + "sha256:db23b1bf8cfe1f7fda19018e7207b20cdb5168f83c437ff7e95d19e39289c447", + "sha256:e0c05aff5c6b1bb5fb46a87e0f9d2f733f83ef6cbbbcd5c642b6c01678268061", + "sha256:e8ac45e8f4eaac0f9f8043ea0e224158855c6a4329fd4ee37c45c61e3beb518e", + "sha256:ea3cd46b6713a10216323cda3333514944e510aa691c945334713fca6b5279ff", + "sha256:ebf7e1e78af38047e08836d33502c7a278915698b7c2145d045f780201679999", + "sha256:f9c11766e7e7c0a2767dda5acb006a118640c9fc0a4104214b96269bfb78399e", + "sha256:ff33c6e6ad006bbc0f34f5faf941cfc62c45841c64c0a058ac38c799f15b5ede" ], "index": "pypi", - "version": "==1.3.18" + "markers": "python_version >= '3.7'", + "version": "==2.0.46" }, - "urllib3": { + "typing-extensions": { "hashes": [ - "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", - "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", + "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" ], - "version": "==1.25.10" + "markers": "python_version >= '3.9'", + "version": "==4.15.0" }, - "websocket-client": { + "urllib3": { "hashes": [ - "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549", - "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010" + "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", + "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4" ], - "version": "==0.57.0" + "markers": "python_version >= '3.9'", + "version": "==2.6.3" } }, "develop": { - "apipkg": { + "asttokens": { "hashes": [ - "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6", - "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c" + "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", + "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7" ], - "version": "==1.5" + "markers": "python_version >= '3.8'", + "version": "==3.0.1" }, - "attrs": { + "blinker": { "hashes": [ - "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", - "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", + "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc" ], - "version": "==19.3.0" - }, - "backcall": { - "hashes": [ - "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", - "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255" - ], - "version": "==0.2.0" + "markers": "python_version >= '3.9'", + "version": "==1.9.0" }, "click": { "hashes": [ - "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", - "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" + "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", + "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6" ], - "version": "==7.1.2" + "markers": "python_version >= '3.10'", + "version": "==8.3.1" }, "coverage": { - "hashes": [ - "sha256:0fc4e0d91350d6f43ef6a61f64a48e917637e1dcfcba4b4b7d543c628ef82c2d", - "sha256:10f2a618a6e75adf64329f828a6a5b40244c1c50f5ef4ce4109e904e69c71bd2", - "sha256:12eaccd86d9a373aea59869bc9cfa0ab6ba8b1477752110cb4c10d165474f703", - "sha256:1874bdc943654ba46d28f179c1846f5710eda3aeb265ff029e0ac2b52daae404", - "sha256:1dcebae667b73fd4aa69237e6afb39abc2f27520f2358590c1b13dd90e32abe7", - "sha256:1e58fca3d9ec1a423f1b7f2aa34af4f733cbfa9020c8fe39ca451b6071237405", - "sha256:214eb2110217f2636a9329bc766507ab71a3a06a8ea30cdeebb47c24dce5972d", - "sha256:25fe74b5b2f1b4abb11e103bb7984daca8f8292683957d0738cd692f6a7cc64c", - "sha256:32ecee61a43be509b91a526819717d5e5650e009a8d5eda8631a59c721d5f3b6", - "sha256:3740b796015b889e46c260ff18b84683fa2e30f0f75a171fb10d2bf9fb91fc70", - "sha256:3b2c34690f613525672697910894b60d15800ac7e779fbd0fccf532486c1ba40", - "sha256:41d88736c42f4a22c494c32cc48a05828236e37c991bd9760f8923415e3169e4", - "sha256:42fa45a29f1059eda4d3c7b509589cc0343cd6bbf083d6118216830cd1a51613", - "sha256:4bb385a747e6ae8a65290b3df60d6c8a692a5599dc66c9fa3520e667886f2e10", - "sha256:509294f3e76d3f26b35083973fbc952e01e1727656d979b11182f273f08aa80b", - "sha256:5c74c5b6045969b07c9fb36b665c9cac84d6c174a809fc1b21bdc06c7836d9a0", - "sha256:60a3d36297b65c7f78329b80120f72947140f45b5c7a017ea730f9112b40f2ec", - "sha256:6f91b4492c5cde83bfe462f5b2b997cdf96a138f7c58b1140f05de5751623cf1", - "sha256:7403675df5e27745571aba1c957c7da2dacb537c21e14007ec3a417bf31f7f3d", - "sha256:87bdc8135b8ee739840eee19b184804e5d57f518578ffc797f5afa2c3c297913", - "sha256:8a3decd12e7934d0254939e2bf434bf04a5890c5bf91a982685021786a08087e", - "sha256:9702e2cb1c6dec01fb8e1a64c015817c0800a6eca287552c47a5ee0ebddccf62", - "sha256:a4d511012beb967a39580ba7d2549edf1e6865a33e5fe51e4dce550522b3ac0e", - "sha256:bbb387811f7a18bdc61a2ea3d102be0c7e239b0db9c83be7bfa50f095db5b92a", - "sha256:bfcc811883699ed49afc58b1ed9f80428a18eb9166422bce3c31a53dba00fd1d", - "sha256:c32aa13cc3fe86b0f744dfe35a7f879ee33ac0a560684fef0f3e1580352b818f", - "sha256:ca63dae130a2e788f2b249200f01d7fa240f24da0596501d387a50e57aa7075e", - "sha256:d54d7ea74cc00482a2410d63bf10aa34ebe1c49ac50779652106c867f9986d6b", - "sha256:d67599521dff98ec8c34cd9652cbcfe16ed076a2209625fca9dc7419b6370e5c", - "sha256:d82db1b9a92cb5c67661ca6616bdca6ff931deceebb98eecbd328812dab52032", - "sha256:d9ad0a988ae20face62520785ec3595a5e64f35a21762a57d115dae0b8fb894a", - "sha256:ebf2431b2d457ae5217f3a1179533c456f3272ded16f8ed0b32961a6d90e38ee", - "sha256:ed9a21502e9223f563e071759f769c3d6a2e1ba5328c31e86830368e8d78bc9c", - "sha256:f50632ef2d749f541ca8e6c07c9928a37f87505ce3a9f20c8446ad310f1aa87b" - ], - "version": "==5.2" + "extras": [ + "toml" + ], + "hashes": [ + "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246", + "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459", + "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129", + "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6", + "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415", + "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf", + "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80", + "sha256:19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11", + "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0", + "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b", + "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9", + "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b", + "sha256:2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f", + "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505", + "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47", + "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55", + "sha256:296f8b0af861d3970c2a4d8c91d48eb4dd4771bcef9baedec6a9b515d7de3def", + "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689", + "sha256:2a09cfa6a5862bc2fc6ca7c3def5b2926194a56b8ab78ffcf617d28911123012", + "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5", + "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3", + "sha256:2cb0f1e000ebc419632bbe04366a8990b6e32c4e0b51543a6484ffe15eaeda95", + "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9", + "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601", + "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997", + "sha256:33901f604424145c6e9c2398684b92e176c0b12df77d52db81c20abd48c3794c", + "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac", + "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c", + "sha256:3998e5a32e62fdf410c0dbd3115df86297995d6e3429af80b8798aad894ca7aa", + "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750", + "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3", + "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d", + "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12", + "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a", + "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932", + "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356", + "sha256:4fc7fa81bbaf5a02801b65346c8b3e657f1d93763e58c0abdf7c992addd81a92", + "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148", + "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39", + "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634", + "sha256:65dfcbe305c3dfe658492df2d85259e0d79ead4177f9ae724b6fb245198f55d6", + "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72", + "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98", + "sha256:71ca20079dd8f27fcf808817e281e90220475cd75115162218d0e27549f95fef", + "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3", + "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9", + "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0", + "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a", + "sha256:784fc3cf8be001197b652d51d3fd259b1e2262888693a4636e18879f613a62a9", + "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552", + "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc", + "sha256:79e73a76b854d9c6088fe5d8b2ebe745f8681c55f7397c3c0a016192d681045f", + "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525", + "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940", + "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a", + "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23", + "sha256:8041b6c5bfdc03257666e9881d33b1abc88daccaf73f7b6340fb7946655cd10f", + "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc", + "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b", + "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056", + "sha256:8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7", + "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb", + "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a", + "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd", + "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea", + "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126", + "sha256:94eb63f9b363180aff17de3e7c8760c3ba94664ea2695c52f10111244d16a299", + "sha256:9d107aff57a83222ddbd8d9ee705ede2af2cc926608b57abed8ef96b50b7e8f9", + "sha256:a32ebc02a1805adf637fc8dec324b5cdacd2e493515424f70ee33799573d661b", + "sha256:a3aa4e7b9e416774b21797365b358a6e827ffadaaca81b69ee02946852449f00", + "sha256:a6f94a7d00eb18f1b6d403c91a88fd58cfc92d4b16080dfdb774afc8294469bf", + "sha256:aa3feb8db2e87ff5e6d00d7e1480ae241876286691265657b500886c98f38bda", + "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2", + "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5", + "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d", + "sha256:b507778ae8a4c915436ed5c2e05b4a6cecfa70f734e19c22a005152a11c7b6a9", + "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9", + "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b", + "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa", + "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092", + "sha256:bb28c0f2cf2782508a40cec377935829d5fcc3ad9a3681375af4e84eb34b6b58", + "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea", + "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26", + "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea", + "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9", + "sha256:d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053", + "sha256:d65b2d373032411e86960604dc4edac91fdfb5dca539461cf2cbe78327d1e64f", + "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0", + "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3", + "sha256:e101609bcbbfb04605ea1027b10dc3735c094d12d40826a60f897b98b1c30256", + "sha256:e24f9156097ff9dc286f2f913df3a7f63c0e333dcafa3c196f2c18b4175ca09a", + "sha256:e2f25215f1a359ab17320b47bcdaca3e6e6356652e8256f2441e4ef972052903", + "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", + "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd", + "sha256:e856bf6616714c3a9fbc270ab54103f4e685ba236fa98c054e8f87f266c93505", + "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7", + "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0", + "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2", + "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a", + "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71", + "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985", + "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242", + "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d", + "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af", + "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c", + "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0" + ], + "markers": "python_version >= '3.10'", + "version": "==7.13.4" }, "decorator": { "hashes": [ - "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760", - "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7" + "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", + "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a" ], - "version": "==4.4.2" + "markers": "python_version >= '3.8'", + "version": "==5.2.1" }, "execnet": { "hashes": [ - "sha256:cacb9df31c9680ec5f95553976c4da484d407e85e41c83cb812aa014f0eddc50", - "sha256:d4efd397930c46415f62f8a31388d6be4f27a91d7550eb79bc64a756e0056547" + "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", + "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec" ], - "version": "==1.7.1" + "markers": "python_version >= '3.8'", + "version": "==2.1.2" + }, + "executing": { + "hashes": [ + "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", + "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017" + ], + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "flask": { "hashes": [ - "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", - "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557" + "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", + "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c" + ], + "markers": "python_version >= '3.9'", + "version": "==3.1.2" + }, + "iniconfig": { + "hashes": [ + "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", + "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12" ], - "version": "==1.1.2" + "markers": "python_version >= '3.10'", + "version": "==2.3.0" }, "ipython": { "hashes": [ - "sha256:2dbcc8c27ca7d3cfe4fcdff7f45b27f9a8d3edfa70ff8024a71c7a8eb5f09d64", - "sha256:9f4fcb31d3b2c533333893b9172264e4821c1ac91839500f31bd43f2c59b3ccf" + "sha256:c6ab68cc23bba8c7e18e9b932797014cc61ea7fd6f19de180ab9ba73e65ee58d", + "sha256:cd9e656be97618a0676d058134cd44e6dc7012c0e5cb36a9ce96a8c904adaf77" ], "index": "pypi", - "version": "==7.16.1" + "markers": "python_version >= '3.11'", + "version": "==9.10.0" }, - "ipython-genutils": { + "ipython-pygments-lexers": { "hashes": [ - "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", - "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" + "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", + "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c" ], - "version": "==0.2.0" + "markers": "python_version >= '3.8'", + "version": "==1.1.1" }, "itsdangerous": { "hashes": [ - "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", - "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" + "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", + "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173" ], - "version": "==1.1.0" + "markers": "python_version >= '3.8'", + "version": "==2.2.0" }, "jedi": { "hashes": [ - "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20", - "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5" + "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", + "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9" ], - "version": "==0.17.2" + "markers": "python_version >= '3.6'", + "version": "==0.19.2" }, "jinja2": { "hashes": [ - "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", - "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" + "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", + "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" ], - "version": "==2.11.2" + "markers": "python_version >= '3.7'", + "version": "==3.1.6" }, "markupsafe": { "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" - ], - "version": "==1.1.1" + "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", + "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", + "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", + "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", + "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", + "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", + "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", + "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", + "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", + "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", + "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", + "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", + "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", + "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", + "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", + "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", + "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", + "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", + "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", + "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", + "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", + "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", + "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", + "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", + "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", + "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", + "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", + "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", + "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", + "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", + "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", + "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", + "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", + "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", + "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", + "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", + "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", + "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", + "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", + "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", + "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", + "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", + "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", + "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", + "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", + "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", + "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", + "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", + "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", + "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", + "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", + "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", + "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", + "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", + "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", + "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", + "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", + "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", + "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", + "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", + "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", + "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", + "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", + "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", + "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", + "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", + "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", + "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", + "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", + "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", + "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", + "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", + "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", + "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", + "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", + "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", + "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", + "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", + "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", + "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", + "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", + "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", + "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", + "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", + "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", + "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", + "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", + "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", + "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50" + ], + "markers": "python_version >= '3.9'", + "version": "==3.0.3" + }, + "matplotlib-inline": { + "hashes": [ + "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", + "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe" + ], + "markers": "python_version >= '3.9'", + "version": "==0.2.1" }, "mock": { "hashes": [ - "sha256:3f9b2c0196c60d21838f307f5825a7b86b678cedc58ab9e50a8988187b4d81e0", - "sha256:dd33eb70232b6118298d516bbcecd26704689c386594f0f3c4f13867b2c56f72" + "sha256:4e460e818629b4b173f32d08bf30d3af8123afbb8e04bb5707a1fd4799e503f0", + "sha256:7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f" ], "index": "pypi", - "version": "==4.0.2" - }, - "more-itertools": { - "hashes": [ - "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5", - "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2" - ], - "version": "==8.4.0" + "markers": "python_version >= '3.6'", + "version": "==5.2.0" }, "packaging": { "hashes": [ - "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", - "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" + "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", + "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529" ], - "version": "==20.4" + "markers": "python_version >= '3.8'", + "version": "==26.0" }, "parso": { "hashes": [ - "sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0", - "sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c" + "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", + "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff" ], - "version": "==0.7.0" + "markers": "python_version >= '3.6'", + "version": "==0.8.6" }, "pexpect": { "hashes": [ - "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", - "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" + "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", + "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f" ], - "markers": "sys_platform != 'win32'", - "version": "==4.8.0" - }, - "pickleshare": { - "hashes": [ - "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", - "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" - ], - "version": "==0.7.5" + "markers": "sys_platform != 'win32' and sys_platform != 'emscripten'", + "version": "==4.9.0" }, "pluggy": { "hashes": [ - "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", - "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", + "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" ], - "version": "==0.13.1" + "markers": "python_version >= '3.9'", + "version": "==1.6.0" }, "prompt-toolkit": { "hashes": [ - "sha256:563d1a4140b63ff9dd587bda9557cffb2fe73650205ab6f4383092fb882e7dc8", - "sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04" + "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", + "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955" ], - "version": "==3.0.5" + "markers": "python_version >= '3.8'", + "version": "==3.0.52" }, "ptyprocess": { "hashes": [ - "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", - "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" + "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", + "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" ], - "version": "==0.6.0" + "version": "==0.7.0" }, - "py": { + "pure-eval": { "hashes": [ - "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2", - "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342" + "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", + "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42" ], - "version": "==1.9.0" + "version": "==0.2.3" }, "pygments": { "hashes": [ - "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", - "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" - ], - "version": "==2.6.1" - }, - "pyparsing": { - "hashes": [ - "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", - "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", + "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" ], - "version": "==2.4.7" + "markers": "python_version >= '3.8'", + "version": "==2.19.2" }, "pytest": { "hashes": [ - "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1", - "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8" + "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", + "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11" ], "index": "pypi", - "version": "==5.4.3" + "markers": "python_version >= '3.10'", + "version": "==9.0.2" }, "pytest-cov": { "hashes": [ - "sha256:1a629dc9f48e53512fcbfda6b07de490c374b0c83c55ff7a1720b3fccff0ac87", - "sha256:6e6d18092dce6fad667cd7020deed816f858ad3b49d5b5e2b1cc1c97a4dba65c" + "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", + "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861" ], "index": "pypi", - "version": "==2.10.0" + "markers": "python_version >= '3.9'", + "version": "==7.0.0" }, "pytest-flask": { "hashes": [ - "sha256:44948d3feab48c69e89b087129cc4db66bad9cb5aa472c08dfc798c69f4eac67", - "sha256:4d5678a045c07317618d80223ea124e21e8acc89dae109542dd1fdf6783d96c2" + "sha256:58be1c97b21ba3c4d47e0a7691eb41007748506c36bf51004f78df10691fa95e", + "sha256:c0e36e6b0fddc3b91c4362661db83fa694d1feb91fa505475be6732b5bc8c253" ], "index": "pypi", - "version": "==1.0.0" - }, - "pytest-forked": { - "hashes": [ - "sha256:42a438336731465c5bd76ab38e1645647ac55914a08b507efbabe8783a08aa6c", - "sha256:65f96334863d9cbe53d21f73e8febc4dd61b8d1fdcac7b487d9af07a5d02a938" - ], - "version": "==1.2.0" + "markers": "python_version >= '3.7'", + "version": "==1.3.0" }, "pytest-localserver": { "hashes": [ - "sha256:3a5427909d1dfda10772c1bae4b9803679c0a8f04adb66c338ac607773bfefc2" + "sha256:2607197f390912ab25525d129ac43c3c875049257368b3fe09b5cd03dcc526af", + "sha256:de526dc5fb26395fb7bbf26bfc14dde5ac3390ba8d8c7de42dfa492d65e0c448" ], "index": "pypi", - "version": "==0.5.0" + "markers": "python_version >= '3.7'", + "version": "==0.10.0" }, "pytest-runner": { "hashes": [ - "sha256:5534b08b133ef9a5e2c22c7886a8f8508c95bb0b0bdc6cc13214f269c3c70d51", - "sha256:96c7e73ead7b93e388c5d614770d2bae6526efd997757d3543fe17b557a0942b" + "sha256:70d4739585a7008f37bf4933c013fdb327b8878a5a69fcbb3316c88882f0f49b", + "sha256:ea326ed6f6613992746062362efab70212089a4209c08d67177b3df1c52cd9f2" ], "index": "pypi", - "version": "==5.2" + "markers": "python_version >= '3.7'", + "version": "==6.0.1" }, "pytest-xdist": { "hashes": [ - "sha256:833b902b16473162cc0572d5bd7b63f8b843df63a4ada9c8af1270d31db17607", - "sha256:9149b4010240643a5e206fd0ece6f0a1cd5955dae014cf04610d359e22e22453" + "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", + "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1" ], "index": "pypi", - "version": "==1.33.0" + "markers": "python_version >= '3.9'", + "version": "==3.8.0" }, - "six": { + "stack-data": { "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", + "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695" ], - "version": "==1.15.0" + "version": "==0.6.3" }, "traitlets": { "hashes": [ - "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44", - "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7" + "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", + "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f" ], - "version": "==4.3.3" + "markers": "python_version >= '3.8'", + "version": "==5.14.3" }, "wcwidth": { "hashes": [ - "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", - "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" + "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", + "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159" ], - "version": "==0.2.5" + "markers": "python_version >= '3.8'", + "version": "==0.6.0" }, "werkzeug": { "hashes": [ - "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", - "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" + "sha256:5111e36e91086ece91f93268bb39b4a35c1e6f1feac762c9c822ded0a4e322dc", + "sha256:6a548b0e88955dd07ccb25539d7d0cc97417ee9e179677d22c7041c8f078ce67" ], - "version": "==1.0.1" + "markers": "python_version >= '3.9'", + "version": "==3.1.5" } } } From e4d4fe80ce6782a52689c76a2fd0b668e2152df1 Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Tue, 17 Feb 2026 11:42:52 -0600 Subject: [PATCH 05/34] worker: Update result class for sqlalchemy updates. --- worker/result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/result.py b/worker/result.py index ec85d18..6d8965d 100644 --- a/worker/result.py +++ b/worker/result.py @@ -21,4 +21,4 @@ from sqlalchemy.schema import Table class Result(Base): __table__ = Table('result', Base.metadata, - autoload=True, autoload_with=engine) + autoload_with=engine) From 4e10d8eb931b3c34a50a5af7f4e111724f68581d Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Tue, 17 Feb 2026 11:48:56 -0600 Subject: [PATCH 06/34] worker: Set package versions in Pipfile to versions compatible with those in the current Piplock file (latest versions at time of writing). --- worker/Pipfile | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/worker/Pipfile b/worker/Pipfile index d1f553f..a15dac9 100644 --- a/worker/Pipfile +++ b/worker/Pipfile @@ -4,21 +4,21 @@ verify_ssl = true name = "pypi" [packages] -redis = "*" -dulwich = "*" -psycopg2 = "*" -sqlalchemy = "*" -docker="*" +redis = "~=7.2.0" +dulwich = "~=1.0.0" +psycopg2 = "~=2.9.11" +sqlalchemy = "~=2.0.46" +docker="~=7.1.0" [dev-packages] -pytest = "*" -pytest-runner = "*" -pytest-flask = "*" -pytest-xdist = "*" -pytest-cov = "*" -pytest-localserver = "*" -ipython = "*" -mock = "*" +pytest = "~=9.0.2" +pytest-runner = "~=6.0.1" +pytest-flask = "~=1.3.0" +pytest-xdist = "~=3.8.0" +pytest-cov = "~=7.0.0" +pytest-localserver = "~=0.10.0" +ipython = "~=9.10.0" +mock = "~=5.2.0" [requires] python_version = "3.14" From 7cad7c7a4663f54fa0b2f6370d896cc84ecbb1d5 Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Wed, 1 Apr 2026 15:14:41 -0500 Subject: [PATCH 07/34] Update reset and restore scripts for docker compose v2 and add basic scripts for purging all images/containers. --- purge-containers.sh | 6 ++++++ purge-images.sh | 6 ++++++ reset.sh | 2 +- restore.sh | 4 ++-- 4 files changed, 15 insertions(+), 3 deletions(-) create mode 100755 purge-containers.sh create mode 100755 purge-images.sh diff --git a/purge-containers.sh b/purge-containers.sh new file mode 100755 index 0000000..afa8fd9 --- /dev/null +++ b/purge-containers.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +while IFS= read -r line; do + docker container rm "$line" + echo "$line" +done < "$1" diff --git a/purge-images.sh b/purge-images.sh new file mode 100755 index 0000000..e88456f --- /dev/null +++ b/purge-images.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +while IFS= read -r line; do + docker image rm "$line" + echo "$line" +done < "$1" diff --git a/reset.sh b/reset.sh index ca8c68f..8db7227 100755 --- a/reset.sh +++ b/reset.sh @@ -1,5 +1,5 @@ #!/bin/bash -docker-compose down +docker compose down docker volume rm dtanm_cctf docker volume rm dtanm_db_data diff --git a/restore.sh b/restore.sh index dbf5999..cc93bbc 100755 --- a/restore.sh +++ b/restore.sh @@ -2,12 +2,12 @@ backup_dir="$(pwd)/backup" -docker-compose down +docker compose down docker volume rm dtanm_cctf docker volume rm dtanm_db_data docker volume create dtanm_cctf docker run --rm -v dtanm_cctf:/recover -v "$backup_dir:/backup" ubuntu bash -c "cd /recover && tar xvf /backup/cctf.tar" -docker-compose up -d +docker compose up -d cat "$backup_dir/dump.sql" | docker exec -i dtanm_db_1 psql -U postgres From 6ba4fa9dcb644d49d39bdf93f40763ebb3519c28 Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Wed, 1 Apr 2026 15:16:57 -0500 Subject: [PATCH 08/34] Initial attempt at updating the web container for latest / recent . --- web/Dockerfile | 6 ++++-- web/Pipfile | 4 +++- web/web/__init__.py | 31 ++++++++++++++++++++-------- web/web/blueprints/admin/__init__.py | 5 +++++ web/web/models/security.py | 16 +++++++------- 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/web/Dockerfile b/web/Dockerfile index 0324e33..988b9e6 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,11 +1,13 @@ -FROM python:3.8.3 +FROM python:3.14 RUN pip install pipenv WORKDIR /server -COPY Pipfile Pipfile.lock /server/ +#COPY Pipfile Pipfile.lock /server/ +COPY Pipfile /server/ RUN pipenv install --deploy +RUN pipenv lock COPY web web diff --git a/web/Pipfile b/web/Pipfile index 0f69c2c..0c5fe74 100644 --- a/web/Pipfile +++ b/web/Pipfile @@ -13,6 +13,8 @@ psycopg2 = "*" bcrypt = "*" dulwich = "*" email-validator = "*" +pytz = "*" +argon2_cffi = "*" [dev-packages] pytest = "*" @@ -25,4 +27,4 @@ ipython = "*" mock = "*" [requires] -python_version = "3.8" +python_version = "3.14" diff --git a/web/web/__init__.py b/web/web/__init__.py index 6f0a2da..f0cce9e 100644 --- a/web/web/__init__.py +++ b/web/web/__init__.py @@ -9,6 +9,8 @@ from functools import wraps import time +from flask_security.models import fsqla_v3 as fsqla + db = SQLAlchemy() redis = None @@ -27,6 +29,8 @@ def create_app(): global user_datastore, redis app = Flask(__name__, instance_relative_config=True) + app.config['DEBUG'] = True + app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev') # Database Config for Flask-Security @@ -49,33 +53,41 @@ def create_app(): app.config.from_pyfile('/pack/config.py', silent=True) # Create database connection object + #db = SQLAlchemy(app) db.init_app(app) + fsqla.FsModels.set_db_info(db) + redis=Redis(host=os.environ.get('REDIS_HOST', 'localhost'), port=os.environ.get('REDIS_PORT', 6379), db=os.environ.get('REDIS_DB', 0)) # Setup Flask-Security from web.models.security import User, Role + from web.models.team import Team user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore) - # Create the administrative user - @app.before_first_request - def create_user(): + with app.app_context(): db.create_all() - user_datastore.find_or_create_role(name='admin', description='Administrator') + admin_role = user_datastore.find_or_create_role(name='admin', description='Administrator') admin_email = app.config['ADMIN_USER_EMAIL'] admin_password = app.config['ADMIN_USER_PASSWORD'] - if not user_datastore.get_user(admin_email): - user_datastore.create_user(email=admin_email, password=admin_password, name="DTANM Administrator") + admin_user = user_datastore.find_user(email=admin_email) + if not admin_user: + admin_user = user_datastore.create_user(email=admin_email, password=admin_password, name="DTANM Administrator") db.session.commit() - user_datastore.add_role_to_user(admin_email, 'admin') + app.logger.info(type(admin_role)) + user_datastore.add_role_to_user(admin_user, admin_role) db.session.commit() + # Create the administrative user + #@app.before_first_request + #def create_user(): + #@app.before_first_request def set_up_filesystem(): import shutil @@ -161,8 +173,9 @@ def update_team_name(): flash("Your team's name has been updated.", "success") return redirect(request.referrer) - @app.before_first_request - def register_pack_attacks(): + #@app.before_first_request + #def register_pack_attacks(): + with app.app_context(): from web.models.attack import Attack, create_attack_from_tar if os.path.exists('/pack/attacks') and Attack.query.count() == 0: attack_names = {} diff --git a/web/web/blueprints/admin/__init__.py b/web/web/blueprints/admin/__init__.py index e5f978b..c6c1886 100644 --- a/web/web/blueprints/admin/__init__.py +++ b/web/web/blueprints/admin/__init__.py @@ -15,6 +15,7 @@ import csv import secrets import string +import random admin = Blueprint('admin', __name__, template_folder='templates') @@ -50,6 +51,9 @@ def add_user(): password = request.form['password'] user.password = hash_password(password) + # Generate unique identifier. + user.fs_uniquifier = random.randbytes(32).hex() + user_datastore.activate_user(user) db.session.add(user) db.session.commit() @@ -191,6 +195,7 @@ def import_users(): user.email = row['Email'] user.password = hash_password(row['Password'] if 'Password' in row else 'password') user.team = team + user.fs_uniquifier = random.randbytes(32).hex() user_datastore.activate_user(user) db.session.add(user) db.session.commit() diff --git a/web/web/models/security.py b/web/web/models/security.py index a431b13..4b734d7 100644 --- a/web/web/models/security.py +++ b/web/web/models/security.py @@ -1,19 +1,20 @@ from web import db from flask_security import UserMixin, RoleMixin +from flask_security.models import fsqla_v3 as fsqla -class RolesUsers(db.Model): - __tablename__ = 'roles_users' - id = db.Column(db.Integer(), primary_key=True) - user_id = db.Column('user_id', db.Integer(), db.ForeignKey('user.id')) - role_id = db.Column('role_id', db.Integer(), db.ForeignKey('role.id')) +#class RolesUsers(db.Model): +# __tablename__ = 'roles_users' +# id = db.Column(db.Integer(), primary_key=True) +# user_id = db.Column('user_id', db.Integer(), db.ForeignKey('user.id')) +# role_id = db.Column('role_id', db.Integer(), db.ForeignKey('role.id')) -class Role(db.Model, RoleMixin): +class Role(db.Model, fsqla.FsRoleMixin): __tablename__ = 'role' id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(80), unique=True) description = db.Column(db.String(255)) -class User(db.Model, UserMixin): +class User(db.Model, fsqla.FsUserMixin): __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True) @@ -31,3 +32,4 @@ class User(db.Model, UserMixin): team_id = db.Column(db.Integer, db.ForeignKey('team.id')) team = db.relationship('Team', back_populates='members') name = db.Column(db.String(255)) + #fs_uniquifier = db.Column(db.String(64)) From bfd06e7df1c9a0cd2229f19ae21e0f20607b014a Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Wed, 1 Apr 2026 15:18:19 -0500 Subject: [PATCH 09/34] worker: Destroy containers when we're done with them, also some stuff with cleaning up the work directory (need to play with this more, but I'm moving dev machines). --- worker/Pipfile.lock | 2 +- worker/run_worker.py | 2 ++ worker/worker.py | 14 ++++++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/worker/Pipfile.lock b/worker/Pipfile.lock index 46eec49..c968725 100644 --- a/worker/Pipfile.lock +++ b/worker/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "7bbed8859ffe551d6b845b71f0dcb84866d8a6eb20efc7912ec4c788ba1d8676" + "sha256": "8ca90350da10e9e17eb4352d8e3ad6ce1c1c6835bdbf8b3a70b1dde4cce57d51" }, "pipfile-spec": 6, "requires": { diff --git a/worker/run_worker.py b/worker/run_worker.py index 9385f9a..c7448c1 100644 --- a/worker/run_worker.py +++ b/worker/run_worker.py @@ -11,6 +11,8 @@ from sqlalchemy.exc import IntegrityError, NoSuchTableError import signal +import traceback + from attack import Attack from utils import are_dirs_same diff --git a/worker/worker.py b/worker/worker.py index 135bf3c..3ffc448 100644 --- a/worker/worker.py +++ b/worker/worker.py @@ -43,6 +43,10 @@ def __enter__(self): self.worktmp_subdir = os.uname()[1] # device hostname self.worktmp_dir = os.path.join('/worktmp', self.worktmp_subdir) + try: + shutil.rmtree(self.worktmp_dir) + except: + pass os.mkdir(self.worktmp_dir) if self.files_dir: @@ -58,7 +62,7 @@ def __enter__(self): def __exit__(self, *args): shutil.rmtree(self.exercise_dir) - shutil.rmtree(self.worktmp_dir) + #shutil.rmtree(self.worktmp_dir) def get_repo_checksum(self) -> Optional[str]: if self.repo: @@ -78,7 +82,7 @@ def run(self) -> Result: dockerfile_path = os.path.join(self.source_dir, "Dockerfile") shutil.copyfile('/pack/Dockerfile.build', dockerfile_path) with open(dockerfile_path, 'a') as f: - f.wirte(f"ENTRYPOINT { os.path.join('/opt/dtanm', self.prog) } $(cat /opt/dtanm/env/args)") + f.write(f"ENTRYPOINT { os.path.join('/opt/dtanm', self.prog) } $(cat /opt/dtanm/env/args)") base_docker_image, logs = client.images.build(path=self.source_dir, tag=self.get_repo_checksum()) logging.getLogger(__name__).info("built dockerfile: " + base_docker_image.id) @@ -119,6 +123,7 @@ def run(self) -> Result: cpu_quota=int(10000 * config.SCORING_MAX_CPUS), detach=True, network_disabled=getattr(config, "SCORING_DISABLE_NETWORK", True), + network_mode="host", mounts=[worktmp_mount]) container_build_elapsed = time.time() - container_build_start @@ -146,6 +151,11 @@ def run(self) -> Result: stdout = container.logs(stdout=True, stderr=False) stderr = container.logs(stdout=False, stderr=True) + try: + container.remove(force=True) + except: + pass # TODO + result = Result() result.commit_hash = self.get_repo_checksum() result.stdout = stdout From 018e35b3126fe35cd54aaedb258f94a95fa757c5 Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Mon, 20 Apr 2026 12:42:01 -0500 Subject: [PATCH 10/34] web: Update model classes to modern sqlalchemy mapped_column etc. --- web/web/__init__.py | 7 ++---- web/web/models/attack.py | 15 ++++++++----- web/web/models/result.py | 46 +++++++++++++++++++++----------------- web/web/models/security.py | 37 ++++++++++++------------------ web/web/models/team.py | 26 +++++++++++---------- 5 files changed, 64 insertions(+), 67 deletions(-) diff --git a/web/web/__init__.py b/web/web/__init__.py index f0cce9e..88e7f9f 100644 --- a/web/web/__init__.py +++ b/web/web/__init__.py @@ -4,14 +4,14 @@ from flask import Flask, request, url_for, render_template, flash, redirect, g from flask_sqlalchemy import SQLAlchemy from flask_security import Security, SQLAlchemyUserDatastore, login_required, current_user +from flask_security.models import fsqla_v3 as fsqla from redis import Redis import pytz from functools import wraps import time -from flask_security.models import fsqla_v3 as fsqla - db = SQLAlchemy() +fsqla.FsModels.set_db_info(db) redis = None user_datastore = None @@ -53,11 +53,8 @@ def create_app(): app.config.from_pyfile('/pack/config.py', silent=True) # Create database connection object - #db = SQLAlchemy(app) db.init_app(app) - fsqla.FsModels.set_db_info(db) - redis=Redis(host=os.environ.get('REDIS_HOST', 'localhost'), port=os.environ.get('REDIS_PORT', 6379), db=os.environ.get('REDIS_DB', 0)) diff --git a/web/web/models/attack.py b/web/web/models/attack.py index b519184..e4ff99b 100644 --- a/web/web/models/attack.py +++ b/web/web/models/attack.py @@ -11,17 +11,20 @@ from web.models.result import Result from sqlalchemy.sql import func, text from typing import List +from sqlalchemy import Integer, String, DateTime, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime class Attack(db.Model): - id = db.Column(db.Integer, primary_key=True) + id: Mapped[int] = mapped_column(primary_key=True) - name = db.Column(db.String(255)) - hash = db.Column(db.String(255)) + name: Mapped[str] = mapped_column(String(255)) + hash: Mapped[str] = mapped_column(String(255)) - team_id = db.Column(db.Integer, db.ForeignKey('team.id')) - team = db.relationship('Team', back_populates="attacks") + team_id: Mapped[int] = mapped_column(ForeignKey('team.id')) + team: Mapped["Team"] = relationship(back_populates="attacks") - created_at = db.Column(db.DateTime, server_default=func.now()) + created_at: Mapped[datetime] = mapped_column(server_default=func.now()) @property def cmd_args(self) -> str: diff --git a/web/web/models/result.py b/web/web/models/result.py index c827f1d..41121e2 100644 --- a/web/web/models/result.py +++ b/web/web/models/result.py @@ -1,49 +1,53 @@ from __future__ import annotations from web import db from sqlalchemy.sql import func +from sqlalchemy import String, ForeignKey, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime +from typing import Optional class Result(db.Model): - id = db.Column(db.Integer, primary_key=True) + id: Mapped[int] = mapped_column(primary_key=True) - attack_id = db.Column(db.Integer, db.ForeignKey('attack.id')) - attack = db.relationship('Attack') + attack_id: Mapped[int] = mapped_column(ForeignKey('attack.id')) + attack: Mapped["Attack"] = relationship() # Result is either from `gold` or from a team - gold = db.Column(db.Boolean()) - team_id = db.Column(db.Integer, db.ForeignKey('team.id')) - team = db.relationship('Team') + gold: Mapped[bool] = mapped_column() + team_id: Mapped[Optional[int]] = mapped_column(ForeignKey('team.id')) + team: Mapped[Optional["Team"]] = relationship() - commit_hash = db.Column(db.String(255)) - created_at = db.Column(db.DateTime(), server_default=func.now()) - passed = db.Column(db.Boolean()) + commit_hash: Mapped[str] = mapped_column(String(255)) + created_at: Mapped[datetime] = mapped_column(server_default=func.now()) + passed: Mapped[Optional[bool]] = mapped_column() # psycopg2 returns memoryview objects, unless it's zero length, in which # case it returns b'', of type bytes. To keep this consistent, we explicitly # cast to bytes here. - _stdout = db.Column("stdout", db.LargeBinary()) + _stdout: Mapped[bytes] = mapped_column("stdout") @property def stdout(self): return bytes(self._stdout) - stdout_hash = db.Column(db.String(64)) - stdout_correct = db.Column(db.Boolean()) + #stdout_hash: Mapped[str] = mapped_column(String(64)) # Unused + stdout_correct: Mapped[Optional[bool]] = mapped_column() - _stderr = db.Column("stderr", db.LargeBinary()) + _stderr: Mapped[bytes] = mapped_column("stderr") @property def stderr(self): return bytes(self._stderr) - stderr_hash = db.Column(db.String(64)) - stderr_correct = db.Column(db.Boolean()) + #stderr_hash: Mapped[str] = mapped_column(String(64)) # Unused + stderr_correct: Mapped[Optional[bool]] = mapped_column() - filesystem_hash = db.Column(db.String(64)) - filesystem_correct = db.Column(db.Boolean()) + #filesystem_hash: Mapped[str] = mapped_column(String(64)) # TODO + #filesystem_correct: Mapped[bool] = mapped_column() - return_code = db.Column(db.Integer()) - return_code_correct = db.Column(db.Boolean()) + return_code: Mapped[int] = mapped_column() + return_code_correct: Mapped[Optional[bool]] = mapped_column() - seconds_to_complete = db.Column(db.Float()) + seconds_to_complete: Mapped[float] = mapped_column() # Contains custom output, such as compilation errors - output = db.Column(db.Text()) + output: Mapped[Optional[str]] = mapped_column(Text()) @property def correct_result(self) -> Result: diff --git a/web/web/models/security.py b/web/web/models/security.py index 4b734d7..29b6d87 100644 --- a/web/web/models/security.py +++ b/web/web/models/security.py @@ -1,35 +1,26 @@ from web import db from flask_security import UserMixin, RoleMixin from flask_security.models import fsqla_v3 as fsqla +from sqlalchemy import String, ForeignKey, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime +from typing import Optional #class RolesUsers(db.Model): # __tablename__ = 'roles_users' -# id = db.Column(db.Integer(), primary_key=True) -# user_id = db.Column('user_id', db.Integer(), db.ForeignKey('user.id')) -# role_id = db.Column('role_id', db.Integer(), db.ForeignKey('role.id')) +# id = mapped_column(db.Integer(), primary_key=True) +# user_id = mapped_column('user_id', db.Integer(), db.ForeignKey('user.id')) +# role_id = mapped_column('role_id', db.Integer(), db.ForeignKey('role.id')) class Role(db.Model, fsqla.FsRoleMixin): __tablename__ = 'role' - id = db.Column(db.Integer(), primary_key=True) - name = db.Column(db.String(80), unique=True) - description = db.Column(db.String(255)) + id: Mapped[int] = mapped_column(primary_key=True) class User(db.Model, fsqla.FsUserMixin): __tablename__ = 'user' - id = db.Column(db.Integer, primary_key=True) - email = db.Column(db.String(255), unique=True) - username = db.Column(db.String(255)) - password = db.Column(db.String(255)) - last_login_at = db.Column(db.DateTime()) - current_login_at = db.Column(db.DateTime()) - last_login_ip = db.Column(db.String(100)) - current_login_ip = db.Column(db.String(100)) - login_count = db.Column(db.Integer) - active = db.Column(db.Boolean()) - confirmed_at = db.Column(db.DateTime()) - roles = db.relationship('Role', secondary='roles_users', - backref=db.backref('users', lazy='dynamic')) - team_id = db.Column(db.Integer, db.ForeignKey('team.id')) - team = db.relationship('Team', back_populates='members') - name = db.Column(db.String(255)) - #fs_uniquifier = db.Column(db.String(64)) + id: Mapped[Optional[int]] = mapped_column(primary_key=True) + + team_id: Mapped[Optional[int]] = mapped_column( db.ForeignKey('team.id')) + team: Mapped[Optional["Team"]] = db.relationship(back_populates='members') + name: Mapped[Optional[str]] = mapped_column(db.String(255)) + diff --git a/web/web/models/team.py b/web/web/models/team.py index 6f2b305..24e7b43 100644 --- a/web/web/models/team.py +++ b/web/web/models/team.py @@ -7,6 +7,10 @@ from web.models.task import add_task from web.models.attack import Attack from web import redis +from sqlalchemy import String, ForeignKey, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime +from typing import List import os import shutil @@ -15,13 +19,13 @@ class Team(db.Model): - id = db.Column(db.Integer, primary_key=True) + id: Mapped[int] = mapped_column(primary_key=True) - name = db.Column(db.String(255)) + name: Mapped[str] = mapped_column(String(255)) - members = db.relationship('User', back_populates="team") - attacks = db.relationship('Attack', back_populates="team") - badges = db.relationship('Badge', back_populates="team") + members: Mapped[List['User']] = relationship(back_populates="team") + attacks: Mapped[List['Attack']] = relationship(back_populates="team") + badges: Mapped[List['Badge']] = relationship(back_populates="team") # Create folders and repositories for teams def set_up_repo(self): @@ -87,8 +91,6 @@ def is_being_scored(self): return True return False - most_passing = db.Column(db.String(255)) # "87/100" - @property def badge_list(self): """ @@ -100,10 +102,10 @@ def badge_list(self): class Badge(db.Model): - id = db.Column(db.Integer, primary_key=True) + id: Mapped[int] = mapped_column(primary_key=True) - team_id = db.Column(db.Integer, db.ForeignKey('team.id')) - team = db.relationship('Team') + team_id: Mapped[int] = mapped_column(ForeignKey('team.id')) + team: Mapped['Team'] = relationship() - type = db.Column(db.String(255)) - content = db.Column(db.String(255)) + type: Mapped[str] = mapped_column(String(255)) + content: Mapped[str] = mapped_column(String(255)) From 9116343e27e8ec18c043664fa933527836240a9a Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Mon, 20 Apr 2026 12:43:00 -0500 Subject: [PATCH 11/34] worker: misc cleanup --- worker/worker.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/worker/worker.py b/worker/worker.py index 3ffc448..7646260 100644 --- a/worker/worker.py +++ b/worker/worker.py @@ -82,23 +82,12 @@ def run(self) -> Result: dockerfile_path = os.path.join(self.source_dir, "Dockerfile") shutil.copyfile('/pack/Dockerfile.build', dockerfile_path) with open(dockerfile_path, 'a') as f: - f.write(f"ENTRYPOINT { os.path.join('/opt/dtanm', self.prog) } $(cat /opt/dtanm/env/args)") + f.write(f"ENTRYPOINT { os.path.join('/opt/dtanm', self.prog) } \"$(cat /opt/dtanm/env/args)\"") base_docker_image, logs = client.images.build(path=self.source_dir, tag=self.get_repo_checksum()) logging.getLogger(__name__).info("built dockerfile: " + base_docker_image.id) container_build_start = time.time() - # Build Docker image of the attack -# with open(os.path.join(self.source_dir, "Dockerfile"), 'w') as f: -# f.write(f"""FROM {docker_image_name} -#WORKDIR /opt/dtanm -##COPY . . # Eventually we'll want to copy over environment files -#ENTRYPOINT { os.path.join('/opt/dtanm', self.prog) } {self.args.decode()} -#""") - #docker_image, logs = client.images.build(path=self.source_dir) - - - with open(os.path.join(self.worktmp_dir, 'args'), 'w') as f: f.write(f"{self.args.decode()}") @@ -123,7 +112,6 @@ def run(self) -> Result: cpu_quota=int(10000 * config.SCORING_MAX_CPUS), detach=True, network_disabled=getattr(config, "SCORING_DISABLE_NETWORK", True), - network_mode="host", mounts=[worktmp_mount]) container_build_elapsed = time.time() - container_build_start From ce7bc4380a4d5095ed9c698cbc7336747d8aeae4 Mon Sep 17 00:00:00 2001 From: Zachary Kirtz Date: Mon, 20 Apr 2026 17:27:18 -0500 Subject: [PATCH 12/34] blueprints: index_table: Don't hardcode the background color on the results table, this looks a bit weird now but before it was basically unreadable in dark mode(white text on white background). --- web/web/blueprints/teams/templates/teams/show_table.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/web/blueprints/teams/templates/teams/show_table.html b/web/web/blueprints/teams/templates/teams/show_table.html index 0179f34..c62998d 100644 --- a/web/web/blueprints/teams/templates/teams/show_table.html +++ b/web/web/blueprints/teams/templates/teams/show_table.html @@ -39,7 +39,7 @@ Output:
{{ result.output }}
{% elif not result.passed %} {% if formatters|length > 1 %} -
+