commit e08d713c0c05ab7d14c9556de6d1a7ec214e4d49
parent cefa774ccd7da7e64f1dc4e847ba5486ade89407
Author: arjoonn <arjoonn@noreply.localhost>
Date: Sat, 14 Jan 2023 15:08:56 +0000
alpha (!19)
Branch auto created by JayporeCI
<details>
<summary>JayporeCi: 🟡 7da9ea9a2d</summary>
```mermaid
flowchart TB
subgraph Pipeline
direction TB
end
subgraph Docker
direction TB
s_Docker(( )) -.-> Docker_0(Jci):::passed
s_Docker(( )) -.-> Docker_1(JciEnv):::passed
end
subgraph Jobs
direction TB
s_Jobs(( )) -.-> Jobs_0(pylint):::passed
s_Jobs(( )) -.-> Jobs_1(pytest):::passed
s_Jobs(( )) -.-> Jobs_2(black):::passed
end
subgraph Publish
direction TB
s_Publish(( )) -.-> Publish_0(DockerHubJcienv):::running
s_Publish(( )) -.-> Publish_1(DockerHubJci):::passed
s_Publish(( )) -.-> Publish_2(PublishDocs):::passed
s_Publish(( )) -.-> Publish_3(PublishPypi):::passed
end
Pipeline ---> Docker
Docker ---> Jobs
Jobs ---> Publish
classDef pending fill:#aaa, color:black, stroke:black,stroke-width:2px,stroke-dasharray: 5 5;
classDef skipped fill:#aaa, color:black, stroke:black,stroke-width:2px;
classDef assigned fill:#ddd, color:black, stroke:black,stroke-width:2px;
classDef running fill:#bae1ff,color:black,stroke:black,stroke-width:2px,stroke-dasharray: 5 5;
classDef passed fill:#88d8b0, color:black, stroke:black;
classDef failed fill:#ff6f69, color:black, stroke:black;
classDef timeout fill:#ffda9e, color:black, stroke:black;
```
Co-authored-by: arjoonn sharma <arjoonn@midpathsoftware.com>
Reviewed-on: https://gitea.midpathsoftware.com/midpath/jaypore_ci/pulls/19
Diffstat:
14 files changed, 670 insertions(+), 285 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -2,3 +2,4 @@
*.env
*.age
dist/
+.hypothesis/
diff --git a/jaypore_ci/executors/__init__.py b/jaypore_ci/executors/__init__.py
@@ -0,0 +1,2 @@
+from .mock import Mock
+from .docker import Docker
diff --git a/jaypore_ci/docker.py b/jaypore_ci/executors/docker.py
diff --git a/jaypore_ci/executors/mock.py b/jaypore_ci/executors/mock.py
@@ -0,0 +1,143 @@
+"""
+A mock executor that actually does not run anything.
+"""
+import uuid
+
+from jaypore_ci.interfaces import Executor
+from jaypore_ci.logging import logger
+
+
+class Mock(Executor):
+ """
+ Run jobs via docker.
+
+ This will:
+ - Create a separate network for each run
+ - Run jobs as part of the network
+ - Clean up all jobs when the pipeline exits.
+ """
+
+ def __init__(self):
+ super().__init__()
+ self.pipe_id = None
+ self.pipeline = None
+ self.__log__ = []
+ self.__status__ = {}
+
+ def logging(self):
+ """
+ Returns a logging instance that has executor specific
+ information bound to it.
+ """
+ return logger.bind(pipe_id=self.pipe_id, network_name=self.get_net())
+
+ def set_pipeline(self, pipeline):
+ """
+ Set executor's pipeline to the given one.
+
+ This will clean up old networks and create new ones.
+ """
+ if self.pipe_id is not None:
+ self.delete_network()
+ self.delete_all_jobs()
+ self.pipe_id = id(pipeline)
+ self.pipeline = pipeline
+ self.create_network()
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.delete_network()
+ self.delete_all_jobs()
+
+ def get_net(self):
+ """
+ Return a network name based on what the curent pipeline is.
+ """
+ return f"jaypore_{self.pipe_id}" if self.pipe_id is not None else None
+
+ def create_network(self):
+ """
+ Will create a docker network.
+
+ If it fails to do so in 3 attempts it will raise an
+ exception and fail.
+ """
+ assert self.pipe_id is not None, "Cannot create network if pipe is not set"
+ return self.get_net()
+
+ def delete_all_jobs(self):
+ """
+ Deletes all jobs associated with the pipeline for this
+ executor.
+
+ It will stop any jobs that are still running.
+ """
+ assert self.pipe_id is not None, "Cannot delete jobs if pipe is not set"
+ job = None
+ for job in self.pipeline.jobs.values():
+ if job.run_id is not None and not job.run_id.startswith("pyrun_"):
+ self.logging().info("Stop job:", run_id=job.run_id)
+ job.check_job(with_update_report=False)
+ if job is not None:
+ job.check_job()
+ self.logging().info("All jobs stopped")
+
+ def delete_network(self):
+ """
+ Delete the network for this executor.
+ """
+ assert self.pipe_id is not None, "Cannot delete network if pipe is not set"
+ self.logging().info("Delete network", net=self.get_net())
+
+ def get_job_name(self, job):
+ """
+ Generates a clean job name slug.
+ """
+ name = "".join(
+ l
+ for l in job.name.lower().replace(" ", "_")
+ if l in "abcdefghijklmnopqrstuvwxyz_1234567890"
+ )
+ return f"{self.get_net()}_{name}"
+
+ def run(self, job: "Job") -> str:
+ """
+ Run the given job and return a docker container ID.
+ """
+ assert self.pipe_id is not None, "Cannot run job if pipe id is not set"
+ self.pipe_id = id(job.pipeline) if self.pipe_id is None else self.pipe_id
+ if not job.is_service:
+ assert job.command
+ name = self.get_job_name(job)
+ if name in self.__status__:
+ return None
+ run_id = uuid.uuid4().hex
+ self.__log__.append((name, run_id, "Start"))
+ self.__log__.append((name, run_id, "Complete"))
+ self.__status__[name] = self.__status__[run_id] = True
+ self.logging().info(
+ "Run job",
+ run_id=run_id,
+ env_vars=job.get_env(),
+ is_service=job.is_service,
+ name=self.get_job_name(job),
+ net=self.get_net(),
+ image=job.image,
+ command=job.command if not job.is_service else None,
+ )
+ return run_id
+
+ def get_status(self, run_id: str) -> (str, str):
+ """
+ Given a run_id, it will get the status for that run.
+ """
+ is_running, exit_code, logs = True, None, ""
+ if run_id in self.__status__:
+ is_running, exit_code, logs = False, 0, ""
+ return is_running, exit_code, logs
+
+ def get_log(self, query):
+ return [
+ (i, name, *log)
+ for i, (name, *log) in enumerate(self.__log__)
+ if name == query
+ ]
diff --git a/jaypore_ci/gitea.py b/jaypore_ci/gitea.py
@@ -1,173 +0,0 @@
-"""
-A gitea remote git host.
-
-This is used to report pipeline status to the remote.
-"""
-import os
-import subprocess
-from pathlib import Path
-from urllib.parse import urlparse
-
-import requests
-from rich import print as rprint
-
-from jaypore_ci.interfaces import Remote
-from jaypore_ci.logging import logger
-
-
-class Gitea(Remote): # pylint: disable=too-many-instance-attributes
- """
- The remote implementation for gitea.
- """
-
- @classmethod
- def from_env(cls):
- """
- Creates a remote instance from the environment.
- It will:
-
- - Find the remote location using `git remote`.
- - Find the current branch
- - Create a new pull request for that branch
- - Allow posting updates using the gitea token provided
- """
- remote = (
- subprocess.check_output(
- "git remote -v | grep push | awk '{print $2}'", shell=True
- )
- .decode()
- .strip()
- )
- assert "https://" in remote, "Only https remotes supported"
- assert ".git" in remote
- remote = urlparse(remote)
- branch = (
- subprocess.check_output(
- r"git branch | grep \* | awk '{print $2}'", shell=True
- )
- .decode()
- .strip()
- )
- os.environ["JAYPORE_COMMIT_BRANCH"] = branch
- sha = subprocess.check_output("git rev-parse HEAD", shell=True).decode().strip()
- os.environ["JAYPORE_COMMIT_SHA"] = sha
- owner = Path(remote.path).parts[1]
- repo = Path(remote.path).parts[2].replace(".git", "")
- token = os.environ["JAYPORE_GITEA_TOKEN"]
- return cls(
- root=f"{remote.scheme}://{remote.netloc}",
- owner=owner,
- repo=repo,
- branch=branch,
- token=token,
- sha=sha,
- )
-
- def __init__(
- self, root, owner, repo, token, branch, sha
- ): # pylint: disable=too-many-arguments
-
- self.root = root
- self.api = f"{root}/api/v1"
- self.owner = owner
- self.repo = repo
- self.token = token
- self.branch = branch
- self.sha = sha
- self.timeout = 10
- self.base_branch = "main"
-
- def logging(self):
- """
- Return's a logging instance with information about gitea bound to it.
- """
- return logger.bind(
- root=self.root, owner=self.owner, repo=self.repo, branch=self.branch
- )
-
- def get_pr_id(self):
- """
- Returns the pull request ID for the current branch.
- """
- r = requests.post(
- f"{self.api}/repos/{self.owner}/{self.repo}/pulls",
- params={"access_token": self.token},
- timeout=self.timeout,
- json={
- "base": self.base_branch,
- "body": "Branch auto created by JayporeCI",
- "head": self.branch,
- "title": self.branch,
- },
- )
- self.logging().debug("Get PR Id", status_code=r.status_code)
- if r.status_code == 409:
- return r.text.split("issue_id:")[1].split(",")[0].strip()
- if r.status_code == 201:
- return self.get_pr_id()
- if r.status_code == 404 and r.json()["message"] == "IsBranchExist":
- self.base_branch = "develop"
- return self.get_pr_id()
- rprint(
- self.api,
- self.owner,
- self.repo,
- self.token,
- self.branch,
- )
- rprint(r.status_code, r.text)
- raise Exception(r)
-
- def publish(self, report: str, status: str):
- """
- Will publish the report to the remote.
-
- :param report: Report to write to remote.
- :param status: One of ["pending", "success", "error", "failure",
- "warning"] This is the dot next to each commit in gitea.
- """
- assert status in ("pending", "success", "error", "failure", "warning")
- issue_id = self.get_pr_id()
- # Get existing PR body
- r = requests.get(
- f"{self.api}/repos/{self.owner}/{self.repo}/pulls/{issue_id}",
- timeout=self.timeout,
- params={"access_token": self.token},
- )
- self.logging().debug("Get existing body", status_code=r.status_code)
- assert r.status_code == 200
- body = r.json()["body"]
- body = (line for line in body.split("\n"))
- prefix = []
- for line in body:
- if "<summary>JayporeCi" in line:
- prefix = prefix[:-1]
- break
- prefix.append(line)
- while prefix and prefix[-1].strip() == "":
- prefix = prefix[:-1]
- prefix.append("")
- # Post new body with report
- report = "\n".join(prefix) + "\n" + report
- r = requests.patch(
- f"{self.api}/repos/{self.owner}/{self.repo}/pulls/{issue_id}",
- data={"body": report},
- timeout=self.timeout,
- params={"access_token": self.token},
- )
- self.logging().debug("Published new report", status_code=r.status_code)
- # Set commit status
- r = requests.post(
- f"{self.api}/repos/{self.owner}/{self.repo}/statuses/{self.sha}",
- json={
- "context": "JayporeCi",
- "description": f"Pipeline status is: {status}",
- "state": status,
- "target_url": f"{self.root}/{self.owner}/{self.repo}/pulls/{issue_id}",
- },
- timeout=self.timeout,
- params={"access_token": self.token},
- )
- self.logging().debug(
- "Published new status", status=status, status_code=r.status_code
- )
diff --git a/jaypore_ci/interfaces.py b/jaypore_ci/interfaces.py
@@ -48,6 +48,10 @@ class Remote:
manager.
"""
+ def __init__(self, *, sha, branch):
+ self.sha = sha
+ self.branch = branch
+
def publish(self, report: str, status: str):
"""
Publish this report somewhere.
@@ -59,3 +63,7 @@ class Remote:
def __exit__(self, exc_type, exc_value, traceback):
pass
+
+ @classmethod
+ def from_env(cls):
+ raise NotImplementedError()
diff --git a/jaypore_ci/jci.py b/jaypore_ci/jci.py
@@ -13,7 +13,7 @@ from contextlib import contextmanager
import structlog
import pendulum
-from jaypore_ci import gitea, docker
+from jaypore_ci import remotes, executors
from jaypore_ci.interfaces import Remote, Executor, TriggerFailed
from jaypore_ci.logging import logger, jaypore_logs
@@ -205,14 +205,16 @@ class Pipeline: # pylint: disable=too-many-instance-attributes
executor: Executor = None,
*,
graph_direction: str = "TB",
+ poll_interval: int = 1,
**kwargs,
) -> "Pipeline":
self.jobs = {}
self.services = []
self.should_pass_called = set()
- self.remote = remote if remote is not None else gitea.Gitea.from_env()
- self.executor = executor if executor is not None else docker.Docker()
+ self.remote = remote if remote is not None else remotes.gitea.Gitea.from_env()
+ self.executor = executor if executor is not None else executors.docker.Docker()
self.graph_direction = graph_direction
+ self.poll_interval = poll_interval
self.executor.set_pipeline(self)
self.stages = ["Pipeline"]
# ---
@@ -405,12 +407,12 @@ flowchart {self.graph_direction}
"""
depends_on = [] if depends_on is None else depends_on
depends_on = [depends_on] if isinstance(depends_on, str) else depends_on
- assert name not in self.jobs
+ assert name not in self.jobs, f"{name} already defined"
kwargs, job_kwargs = dict(self.pipe_kwargs), kwargs
kwargs.update(self.stage_kwargs if self.stage_kwargs is not None else {})
kwargs.update(job_kwargs)
if not kwargs.get("is_service"):
- assert command
+ assert command, f"Command: {command}"
job = Job(
name=name if name is not None else " ",
command=command,
@@ -439,21 +441,26 @@ flowchart {self.graph_direction}
for values in product(*[kwargs[key] for key in keys]):
yield dict(list(zip(keys, values)))
+ def __ensure_duplex__(self):
+ for name, job in self.jobs.items():
+ for parent_name in job.parents:
+ parent = self.jobs[parent_name]
+ parent.children = list(sorted(set(parent.children).union(set([name]))))
+
def run(self):
"""
Run the pipeline. This is almost always called automatically when the
context of the pipeline declaration finishes.
"""
- # Ensure duplex connection between all nodes
- for name, job in self.jobs.items():
- for parent_name in job.parents:
- parent = self.jobs[parent_name]
- parent.children = list(sorted(set(parent.children).union(set([name]))))
+ self.__ensure_duplex__()
# Run stages one by one
+ job = None
for stage in self.stages:
# --- Trigger starting jobs
jobs = {name: job for name, job in self.jobs.items() if job.stage == stage}
- for name in {job.name for job in jobs.values() if job.parents}:
+ for name in {
+ job.name for job in jobs.values() if job.parents and not job.children
+ }:
jobs[name].trigger()
# --- monitor and ensure all jobs run
while not all(job.is_complete() for job in jobs.values()):
@@ -474,7 +481,7 @@ flowchart {self.graph_direction}
):
job.status = Status.SKIPPED
job.check_job()
- time.sleep(1)
+ time.sleep(self.poll_interval)
# --- has this stage passed?
if not all(
job.is_complete() and job.status == Status.PASSED
@@ -484,7 +491,8 @@ flowchart {self.graph_direction}
job.update_report()
break
self.logging().error("Pipeline passed")
- job.update_report()
+ if job is not None:
+ job.update_report()
@contextmanager
def stage(self, name, **kwargs):
diff --git a/jaypore_ci/remotes/__init__.py b/jaypore_ci/remotes/__init__.py
@@ -0,0 +1,2 @@
+from .mock import Mock
+from .gitea import Gitea
diff --git a/jaypore_ci/remotes/gitea.py b/jaypore_ci/remotes/gitea.py
@@ -0,0 +1,172 @@
+"""
+A gitea remote git host.
+
+This is used to report pipeline status to the remote.
+"""
+import os
+import subprocess
+from pathlib import Path
+from urllib.parse import urlparse
+
+import requests
+from rich import print as rprint
+
+from jaypore_ci.interfaces import Remote
+from jaypore_ci.logging import logger
+
+
+class Gitea(Remote): # pylint: disable=too-many-instance-attributes
+ """
+ The remote implementation for gitea.
+ """
+
+ @classmethod
+ def from_env(cls):
+ """
+ Creates a remote instance from the environment.
+ It will:
+
+ - Find the remote location using `git remote`.
+ - Find the current branch
+ - Create a new pull request for that branch
+ - Allow posting updates using the gitea token provided
+ """
+ remote = (
+ subprocess.check_output(
+ "git remote -v | grep push | awk '{print $2}'", shell=True
+ )
+ .decode()
+ .strip()
+ )
+ assert "https://" in remote, "Only https remotes supported"
+ assert ".git" in remote
+ remote = urlparse(remote)
+ branch = (
+ subprocess.check_output(
+ r"git branch | grep \* | awk '{print $2}'", shell=True
+ )
+ .decode()
+ .strip()
+ )
+ os.environ["JAYPORE_COMMIT_BRANCH"] = branch
+ sha = subprocess.check_output("git rev-parse HEAD", shell=True).decode().strip()
+ os.environ["JAYPORE_COMMIT_SHA"] = sha
+ owner = Path(remote.path).parts[1]
+ repo = Path(remote.path).parts[2].replace(".git", "")
+ token = os.environ["JAYPORE_GITEA_TOKEN"]
+ return cls(
+ root=f"{remote.scheme}://{remote.netloc}",
+ owner=owner,
+ repo=repo,
+ branch=branch,
+ token=token,
+ sha=sha,
+ )
+
+ def __init__(
+ self, *, root, owner, repo, token, **kwargs
+ ): # pylint: disable=too-many-arguments
+ super().__init__(**kwargs)
+ # --- customer
+ self.root = root
+ self.api = f"{root}/api/v1"
+ self.owner = owner
+ self.repo = repo
+ self.token = token
+ self.timeout = 10
+ self.base_branch = "main"
+
+ def logging(self):
+ """
+ Return's a logging instance with information about gitea bound to it.
+ """
+ return logger.bind(
+ root=self.root, owner=self.owner, repo=self.repo, branch=self.branch
+ )
+
+ def get_pr_id(self):
+ """
+ Returns the pull request ID for the current branch.
+ """
+ r = requests.post(
+ f"{self.api}/repos/{self.owner}/{self.repo}/pulls",
+ params={"access_token": self.token},
+ timeout=self.timeout,
+ json={
+ "base": self.base_branch,
+ "body": "Branch auto created by JayporeCI",
+ "head": self.branch,
+ "title": self.branch,
+ },
+ )
+ self.logging().debug("Get PR Id", status_code=r.status_code)
+ if r.status_code == 409:
+ return r.text.split("issue_id:")[1].split(",")[0].strip()
+ if r.status_code == 201:
+ return self.get_pr_id()
+ if r.status_code == 404 and r.json()["message"] == "IsBranchExist":
+ self.base_branch = "develop"
+ return self.get_pr_id()
+ rprint(
+ self.api,
+ self.owner,
+ self.repo,
+ self.token,
+ self.branch,
+ )
+ rprint(r.status_code, r.text)
+ raise Exception(r)
+
+ def publish(self, report: str, status: str):
+ """
+ Will publish the report to the remote.
+
+ :param report: Report to write to remote.
+ :param status: One of ["pending", "success", "error", "failure",
+ "warning"] This is the dot next to each commit in gitea.
+ """
+ assert status in ("pending", "success", "error", "failure", "warning")
+ issue_id = self.get_pr_id()
+ # Get existing PR body
+ r = requests.get(
+ f"{self.api}/repos/{self.owner}/{self.repo}/pulls/{issue_id}",
+ timeout=self.timeout,
+ params={"access_token": self.token},
+ )
+ self.logging().debug("Get existing body", status_code=r.status_code)
+ assert r.status_code == 200
+ body = r.json()["body"]
+ body = (line for line in body.split("\n"))
+ prefix = []
+ for line in body:
+ if "<summary>JayporeCi" in line:
+ prefix = prefix[:-1]
+ break
+ prefix.append(line)
+ while prefix and prefix[-1].strip() == "":
+ prefix = prefix[:-1]
+ prefix.append("")
+ # Post new body with report
+ report = "\n".join(prefix) + "\n" + report
+ r = requests.patch(
+ f"{self.api}/repos/{self.owner}/{self.repo}/pulls/{issue_id}",
+ data={"body": report},
+ timeout=self.timeout,
+ params={"access_token": self.token},
+ )
+ self.logging().debug("Published new report", status_code=r.status_code)
+ # Set commit status
+ r = requests.post(
+ f"{self.api}/repos/{self.owner}/{self.repo}/statuses/{self.sha}",
+ json={
+ "context": "JayporeCi",
+ "description": f"Pipeline status is: {status}",
+ "state": status,
+ "target_url": f"{self.root}/{self.owner}/{self.repo}/pulls/{issue_id}",
+ },
+ timeout=self.timeout,
+ params={"access_token": self.token},
+ )
+ self.logging().debug(
+ "Published new status", status=status, status_code=r.status_code
+ )
diff --git a/jaypore_ci/remotes/mock.py b/jaypore_ci/remotes/mock.py
@@ -0,0 +1,41 @@
+"""
+A gitea remote git host.
+
+This is used to report pipeline status to the remote.
+"""
+import os
+
+
+from jaypore_ci.interfaces import Remote
+from jaypore_ci.logging import logger
+
+
+class Mock(Remote): # pylint: disable=too-many-instance-attributes
+ """
+ A mock remote implementation.
+ """
+
+ @classmethod
+ def from_env(cls):
+ return cls(branch=os.environ["JAYPORE_BRANCH"], sha=os.environ["JAYPORE_SHA"])
+
+ def logging(self):
+ """
+ Return's a logging instance with information about gitea bound to it.
+ """
+ return logger.bind(branch=self.branch)
+
+ def get_pr_id(self):
+ """
+ Returns the pull request ID for the current branch.
+ """
+ return self.branch
+
+ def publish(self, report: str, status: str):
+ """
+ Will publish the report to the remote.
+ """
+ pr_id = self.get_pr_id()
+ self.logging().debug(
+ "Published report", report=report, status=status, pr_id=pr_id
+ )
diff --git a/poetry.lock b/poetry.lock
@@ -2,31 +2,31 @@
[[package]]
name = "alabaster"
-version = "0.7.12"
+version = "0.7.13"
description = "A configurable sidebar-enabled Sphinx theme"
-category = "main"
+category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.6"
files = [
- {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
- {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
+ {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"},
+ {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"},
]
[[package]]
name = "astroid"
-version = "2.12.13"
+version = "2.13.2"
description = "An abstract syntax tree for Python with inference support."
category = "dev"
optional = false
python-versions = ">=3.7.2"
files = [
- {file = "astroid-2.12.13-py3-none-any.whl", hash = "sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907"},
- {file = "astroid-2.12.13.tar.gz", hash = "sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7"},
+ {file = "astroid-2.13.2-py3-none-any.whl", hash = "sha256:8f6a8d40c4ad161d6fc419545ae4b2f275ed86d1c989c97825772120842ee0d2"},
+ {file = "astroid-2.13.2.tar.gz", hash = "sha256:3bc7834720e1a24ca797fd785d77efb14f7a28ee8e635ef040b6e2d80ccb3303"},
]
[package.dependencies]
lazy-object-proxy = ">=1.4.0"
-typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""}
+typing-extensions = ">=4.0.0"
wrapt = [
{version = ">=1.11,<2", markers = "python_version < \"3.11\""},
{version = ">=1.14,<2", markers = "python_version >= \"3.11\""},
@@ -55,7 +55,7 @@ tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy
name = "babel"
version = "2.11.0"
description = "Internationalization utilities"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.6"
files = [
@@ -116,19 +116,102 @@ files = [
[[package]]
name = "charset-normalizer"
-version = "2.1.1"
+version = "3.0.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
-python-versions = ">=3.6.0"
+python-versions = "*"
files = [
- {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
- {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
+ {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"},
+ {file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"},
+ {file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"},
+ {file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"},
+ {file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"},
+ {file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"},
+ {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"},
+ {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"},
]
-[package.extras]
-unicode-backport = ["unicodedata2"]
-
[[package]]
name = "click"
version = "8.1.3"
@@ -190,7 +273,7 @@ graph = ["objgraph (>=1.7.2)"]
name = "docutils"
version = "0.19"
description = "Docutils -- Python Documentation Utilities"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@@ -200,20 +283,53 @@ files = [
[[package]]
name = "exceptiongroup"
-version = "1.0.4"
+version = "1.1.0"
description = "Backport of PEP 654 (exception groups)"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"},
- {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"},
+ {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"},
+ {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"},
]
[package.extras]
test = ["pytest (>=6)"]
[[package]]
+name = "hypothesis"
+version = "6.62.1"
+description = "A library for property-based testing"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "hypothesis-6.62.1-py3-none-any.whl", hash = "sha256:d00a4a9c54b0b8b4570fe1abe42395807a973b4a507e6718309800e6f84e160d"},
+ {file = "hypothesis-6.62.1.tar.gz", hash = "sha256:7d1e2f9871e6509662da317adf9b4aabd6b38280fb6c7930aa4f574d2ed25150"},
+]
+
+[package.dependencies]
+attrs = ">=19.2.0"
+exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
+sortedcontainers = ">=2.1.0,<3.0.0"
+
+[package.extras]
+all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "django (>=3.2)", "dpcontracts (>=0.4)", "importlib-metadata (>=3.6)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=1.0)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2022.7)"]
+cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"]
+codemods = ["libcst (>=0.3.16)"]
+dateutil = ["python-dateutil (>=1.4)"]
+django = ["django (>=3.2)"]
+dpcontracts = ["dpcontracts (>=0.4)"]
+ghostwriter = ["black (>=19.10b0)"]
+lark = ["lark (>=0.10.1)"]
+numpy = ["numpy (>=1.9.0)"]
+pandas = ["pandas (>=1.0)"]
+pytest = ["pytest (>=4.6)"]
+pytz = ["pytz (>=2014.1)"]
+redis = ["redis (>=3.0.0)"]
+zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.7)"]
+
+[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
@@ -229,7 +345,7 @@ files = [
name = "imagesize"
version = "1.4.1"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
@@ -239,14 +355,14 @@ files = [
[[package]]
name = "importlib-metadata"
-version = "5.2.0"
+version = "6.0.0"
description = "Read metadata from Python packages"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "importlib_metadata-5.2.0-py3-none-any.whl", hash = "sha256:0eafa39ba42bf225fc00e67f701d71f85aead9f878569caf13c3724f704b970f"},
- {file = "importlib_metadata-5.2.0.tar.gz", hash = "sha256:404d48d62bba0b7a77ff9d405efd91501bef2e67ff4ace0bed40a0cf28c3c7cd"},
+ {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"},
+ {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"},
]
[package.dependencies]
@@ -259,14 +375,14 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag
[[package]]
name = "iniconfig"
-version = "1.1.1"
-description = "iniconfig: brain-dead simple config-ini parsing"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
files = [
- {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
- {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
+ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
@@ -291,7 +407,7 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"]
name = "jinja2"
version = "3.1.2"
description = "A very fast and expressive template engine."
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@@ -307,38 +423,55 @@ i18n = ["Babel (>=2.7)"]
[[package]]
name = "lazy-object-proxy"
-version = "1.8.0"
+version = "1.9.0"
description = "A fast and thorough lazy object proxy."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "lazy-object-proxy-1.8.0.tar.gz", hash = "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156"},
- {file = "lazy_object_proxy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe"},
- {file = "lazy_object_proxy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25"},
- {file = "lazy_object_proxy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"},
- {file = "lazy_object_proxy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7"},
- {file = "lazy_object_proxy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e"},
- {file = "lazy_object_proxy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d"},
- {file = "lazy_object_proxy-1.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c"},
- {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win32.whl", hash = "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd"},
- {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858"},
- {file = "lazy_object_proxy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada"},
- {file = "lazy_object_proxy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f"},
- {file = "lazy_object_proxy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c"},
- {file = "lazy_object_proxy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288"},
- {file = "lazy_object_proxy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f"},
- {file = "lazy_object_proxy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0"},
- {file = "lazy_object_proxy-1.8.0-pp37-pypy37_pp73-any.whl", hash = "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891"},
- {file = "lazy_object_proxy-1.8.0-pp38-pypy38_pp73-any.whl", hash = "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec"},
- {file = "lazy_object_proxy-1.8.0-pp39-pypy39_pp73-any.whl", hash = "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8"},
+ {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"},
]
[[package]]
name = "markupsafe"
version = "2.1.1"
description = "Safely add untrusted strings to HTML/XML markup."
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@@ -410,14 +543,14 @@ files = [
[[package]]
name = "packaging"
-version = "22.0"
+version = "23.0"
description = "Core utilities for Python packages"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"},
- {file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"},
+ {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"},
+ {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"},
]
[[package]]
@@ -469,19 +602,19 @@ pytzdata = ">=2020.1"
[[package]]
name = "platformdirs"
-version = "2.6.0"
+version = "2.6.2"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"},
- {file = "platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"},
+ {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"},
+ {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"},
]
[package.extras]
-docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"]
-test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
+docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
[[package]]
name = "pluggy"
@@ -501,14 +634,14 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pygments"
-version = "2.13.0"
+version = "2.14.0"
description = "Pygments is a syntax highlighting package written in Python."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
- {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"},
- {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"},
+ {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"},
+ {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"},
]
[package.extras]
@@ -516,14 +649,14 @@ plugins = ["importlib-metadata"]
[[package]]
name = "pylint"
-version = "2.15.9"
+version = "2.15.10"
description = "python code static checker"
category = "dev"
optional = false
python-versions = ">=3.7.2"
files = [
- {file = "pylint-2.15.9-py3-none-any.whl", hash = "sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb"},
- {file = "pylint-2.15.9.tar.gz", hash = "sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4"},
+ {file = "pylint-2.15.10-py3-none-any.whl", hash = "sha256:9df0d07e8948a1c3ffa3b6e2d7e6e63d9fb457c5da5b961ed63106594780cc7e"},
+ {file = "pylint-2.15.10.tar.gz", hash = "sha256:b3dc5ef7d33858f297ac0d06cc73862f01e4f2e74025ec3eff347ce0bc60baf5"},
]
[package.dependencies]
@@ -546,14 +679,14 @@ testutils = ["gitpython (>3)"]
[[package]]
name = "pytest"
-version = "7.2.0"
+version = "7.2.1"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"},
- {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"},
+ {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"},
+ {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"},
]
[package.dependencies]
@@ -585,14 +718,14 @@ six = ">=1.5"
[[package]]
name = "pytz"
-version = "2022.7"
+version = "2022.7.1"
description = "World timezone definitions, modern and historical"
-category = "main"
+category = "dev"
optional = false
python-versions = "*"
files = [
- {file = "pytz-2022.7-py2.py3-none-any.whl", hash = "sha256:93007def75ae22f7cd991c84e02d434876818661f8df9ad5df9e950ff4e52cfd"},
- {file = "pytz-2022.7.tar.gz", hash = "sha256:7ccfae7b4b2c067464a6733c6261673fdb8fd1be905460396b97a073e9fa683a"},
+ {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"},
+ {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"},
]
[[package]]
@@ -609,19 +742,19 @@ files = [
[[package]]
name = "requests"
-version = "2.28.1"
+version = "2.28.2"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.7, <4"
files = [
- {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
- {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
+ {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"},
+ {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"},
]
[package.dependencies]
certifi = ">=2017.4.17"
-charset-normalizer = ">=2,<3"
+charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"
@@ -665,7 +798,7 @@ files = [
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
-category = "main"
+category = "dev"
optional = false
python-versions = "*"
files = [
@@ -674,10 +807,22 @@ files = [
]
[[package]]
+name = "sortedcontainers"
+version = "2.4.0"
+description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
+ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
+]
+
+[[package]]
name = "sphinx"
version = "5.3.0"
description = "Python documentation generator"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.6"
files = [
@@ -711,14 +856,14 @@ test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"]
[[package]]
name = "sphinxcontrib-applehelp"
-version = "1.0.2"
-description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
-category = "main"
+version = "1.0.3"
+description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
+category = "dev"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
files = [
- {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
- {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
+ {file = "sphinxcontrib.applehelp-1.0.3-py3-none-any.whl", hash = "sha256:ba0f2a22e6eeada8da6428d0d520215ee8864253f32facf958cca81e426f661d"},
+ {file = "sphinxcontrib.applehelp-1.0.3.tar.gz", hash = "sha256:83749f09f6ac843b8cb685277dbc818a8bf2d76cc19602699094fe9a74db529e"},
]
[package.extras]
@@ -729,7 +874,7 @@ test = ["pytest"]
name = "sphinxcontrib-devhelp"
version = "1.0.2"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@@ -745,7 +890,7 @@ test = ["pytest"]
name = "sphinxcontrib-htmlhelp"
version = "2.0.0"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.6"
files = [
@@ -761,7 +906,7 @@ test = ["html5lib", "pytest"]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
description = "A sphinx extension which renders display math in HTML via JavaScript"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@@ -776,7 +921,7 @@ test = ["flake8", "mypy", "pytest"]
name = "sphinxcontrib-qthelp"
version = "1.0.3"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@@ -792,7 +937,7 @@ test = ["pytest"]
name = "sphinxcontrib-serializinghtml"
version = "1.1.5"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@@ -860,14 +1005,14 @@ files = [
[[package]]
name = "urllib3"
-version = "1.26.13"
+version = "1.26.14"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [
- {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"},
- {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"},
+ {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"},
+ {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"},
]
[package.extras]
@@ -953,7 +1098,7 @@ files = [
name = "zipp"
version = "3.11.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@@ -968,4 +1113,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
-content-hash = "9212f02d5809a8b441868a98cb2f0c7b3d3c8d2bde53825e45be1fc86b3b9275"
+content-hash = "1a17e8d987ff475351006f7c4837e615822db4a8bcb54764aa84a38e78a8e4e8"
diff --git a/pyproject.toml b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "jaypore_ci"
-version = "0.1.1"
+version = "0.1.2"
description = ""
authors = ["arjoonn sharma <arjoonn.94@gmail.com>"]
@@ -11,12 +11,13 @@ click = "^8.1.3"
pendulum = "^2.1.2"
rich = "^12.6.0"
structlog = "^22.3.0"
-sphinx = "^5.3.0"
[tool.poetry.group.dev.dependencies]
pylint = "^2.15.7"
black = "^22.10.0"
pytest = "^7.2.0"
+sphinx = "^5.3.0"
+hypothesis = "^6.62.1"
[build-system]
requires = ["poetry-core>=1.0.0"]
diff --git a/tests/conftest.py b/tests/conftest.py
@@ -0,0 +1,10 @@
+import pytest
+
+from jaypore_ci import jci, executors, remotes
+
+
+@pytest.fixture(scope="function")
+def pipeline():
+ executor = executors.Mock()
+ remote = remotes.Mock(branch="test_branch", sha="fake")
+ yield jci.Pipeline(executor=executor, remote=remote, poll_interval=0)
diff --git a/tests/test_jaypore_ci.py b/tests/test_jaypore_ci.py
@@ -1,5 +1,30 @@
+import pytest
+
from jaypore_ci import __version__
def test_version():
assert __version__ == "0.1.0"
+
+
+def test_simple_linear_jobs(pipeline):
+ with pipeline as p:
+ p.job("lint", "x")
+ p.job("test", "x", depends_on=["lint"])
+ lint_i = [i for i, *_ in pipeline.executor.get_log("lint")]
+ test_i = [i for i, *_ in pipeline.executor.get_log("test")]
+ assert all(lint < test for lint in lint_i for test in test_i)
+
+
+def test_no_duplicate_names(pipeline):
+ with pytest.raises(AssertionError):
+ with pipeline as p:
+ p.job("lint", "x")
+ p.job("lint", "y")
+
+
+def test_dependency_has_to_be_defined_before_child(pipeline):
+ with pytest.raises(AssertionError):
+ with pipeline as p:
+ p.job("x", "x", depends_on=["y"])
+ p.job("y", "y")