From 10f5cd93513c8e70b2f6d1b64c1730cf132479dc Mon Sep 17 00:00:00 2001 From: grimadas Date: Wed, 29 Dec 2021 16:51:43 +0100 Subject: [PATCH] Remove p2psimpy: --- .gitignore | 156 ++ 01_Intro_To_Distributed_Systems.ipynb | 41 +- 02_Gossip_Services.ipynb | 73 +- 03_Faults.ipynb | 12 +- 04_Byzantine.ipynb | 8 +- 05_Consensus.ipynb | 10 +- binder/environment.yml | 2 +- p2psimpy/__init__.py | 7 - p2psimpy/config.py | 173 --- p2psimpy/consts.py | 5 - p2psimpy/defaults.py | 19 - p2psimpy/logger.py | 19 - p2psimpy/messages.py | 93 -- p2psimpy/network.py | 43 - p2psimpy/peer.py | 249 ---- p2psimpy/peer_factory.py | 47 - p2psimpy/services/__init__.py | 6 - p2psimpy/services/base.py | 85 -- p2psimpy/services/connection_manager.py | 203 --- p2psimpy/services/consensus/lottery.py | 158 -- p2psimpy/services/disruption.py | 111 -- p2psimpy/services/gossip.py | 223 --- p2psimpy/services/message_producer.py | 65 - p2psimpy/services/selection.py | 11 - p2psimpy/simulation.py | 290 ---- p2psimpy/storage/__init__.py | 2 - p2psimpy/storage/dag.py | 24 - p2psimpy/storage/simple.py | 69 - p2psimpy/tests/configspec.ini | 5 - p2psimpy/tests/test.ini | 5 - p2psimpy/tests/test.yaml | 8 - p2psimpy/tests/test_config.py | 30 - p2psimpy/tests/test_connection.py | 40 - p2psimpy/tests/test_simulation.py | 52 - p2psimpy/utils.py | 95 -- p2psimpy/visual.py | 45 - poetry.lock | 1763 +++++++++++++++++++++++ pyproject.toml | 25 + 38 files changed, 2020 insertions(+), 2252 deletions(-) create mode 100644 .gitignore delete mode 100644 p2psimpy/__init__.py delete mode 100644 p2psimpy/config.py delete mode 100644 p2psimpy/consts.py delete mode 100644 p2psimpy/defaults.py delete mode 100644 p2psimpy/logger.py delete mode 100644 p2psimpy/messages.py delete mode 100644 p2psimpy/network.py delete mode 100644 p2psimpy/peer.py delete mode 100644 p2psimpy/peer_factory.py delete mode 100644 p2psimpy/services/__init__.py delete mode 100644 p2psimpy/services/base.py delete mode 100644 p2psimpy/services/connection_manager.py delete mode 100644 p2psimpy/services/consensus/lottery.py delete mode 100644 p2psimpy/services/disruption.py delete mode 100644 p2psimpy/services/gossip.py delete mode 100644 p2psimpy/services/message_producer.py delete mode 100644 p2psimpy/services/selection.py delete mode 100644 p2psimpy/simulation.py delete mode 100644 p2psimpy/storage/__init__.py delete mode 100644 p2psimpy/storage/dag.py delete mode 100644 p2psimpy/storage/simple.py delete mode 100644 p2psimpy/tests/configspec.ini delete mode 100644 p2psimpy/tests/test.ini delete mode 100644 p2psimpy/tests/test.yaml delete mode 100644 p2psimpy/tests/test_config.py delete mode 100644 p2psimpy/tests/test_connection.py delete mode 100644 p2psimpy/tests/test_simulation.py delete mode 100644 p2psimpy/utils.py delete mode 100644 p2psimpy/visual.py create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd85c5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,156 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +.python-version + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +**/.DS_Store + diff --git a/01_Intro_To_Distributed_Systems.ipynb b/01_Intro_To_Distributed_Systems.ipynb index 6681a17..db1f0a7 100644 --- a/01_Intro_To_Distributed_Systems.ipynb +++ b/01_Intro_To_Distributed_Systems.ipynb @@ -60,13 +60,6 @@ "This class can be used as generators of parameters, saved and loaded as a [YAML](https://en.wikipedia.org/wiki/YAML) file." ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -243,7 +236,9 @@ " 2. Get 100 samples from the configuration with latencies\n", " \n", " 3. Visualize in a grid of histograms\n", - "\n" + "\n", + "\n", + "The visulations shows the latency matrix for our two locations: \n" ] }, { @@ -270,23 +265,13 @@ " .rename(columns={'level_0':'from', 'level_1':'to', 0: 'value'})\n", " \n", "# 2. Get 100 samples\n", - "df = pd.concat(prepare(DistLocations.get()['latencies']) for _ in range(100))\n", + "df = pd.concat([prepare(DistLocations.get()['latencies']) for _ in range(100)], ignore_index=True)\n", + "\n", + "#df \n", "\n", "# 3. Visualize in a grid of histograms\n", "g = sns.FacetGrid(df, col='to', row='from', height=4, aspect=1)\n", - "g = g.map(sns.distplot, 'value')\n" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The visulations shows the latency matrix for our two locations. " + "g = g.map(sns.histplot, 'value')\n" ] }, { @@ -529,10 +514,10 @@ }, "outputs": [], "source": [ - "from p2psimpy.simulation import BaseSimulation\n", + "from p2psimpy.simulation import Simulation\n", "\n", "# Create BaseSimulation with enabled logger and save in the logs directory. \n", - "sim = BaseSimulation(Locations, G, peer_types, enable_logger=True, logger_dir='logs')\n", + "sim = Simulation(Locations, G, peer_types, enable_logger=True, logger_dir='logs')\n", "\n", "# Let's run the simulation for 5 seconds\n", "sim.run(5_000)" @@ -618,7 +603,7 @@ }, "outputs": [], "source": [ - "sim = BaseSimulation(Locations, topology_specs, peer_types, logger_dir='logs2')\n", + "sim = Simulation(Locations, topology_specs, peer_types, logger_dir='logs2')\n", "\n", "# Let's run the simulation for 5 seconds\n", "sim.run(5_000)" @@ -642,7 +627,7 @@ }, "outputs": [], "source": [ - "G1 = sim.get_graph()\n", + "G1 = sim.current_topology()\n", "nx.draw_networkx(G1)" ] }, @@ -693,7 +678,7 @@ "metadata": { "hide_input": false, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -707,7 +692,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.9" }, "pycharm": { "stem_cell": { diff --git a/02_Gossip_Services.ipynb b/02_Gossip_Services.ipynb index b40c4ae..8429f9f 100644 --- a/02_Gossip_Services.ipynb +++ b/02_Gossip_Services.ipynb @@ -68,7 +68,6 @@ "from p2psimpy import *\n", "from p2psimpy.services.base import BaseHandler\n", "from p2psimpy.messages import BaseMessage\n", - "from p2psimpy.storage import Storage\n", "\n", "from re import split \n", "from copy import copy\n", @@ -99,13 +98,20 @@ " self.exclude_types = exclude_types\n", " \n", " self.strg_name = 'msg_time'\n", - " self.peer.add_storage(self.strg_name, Storage())\n", + " self.times_name = 'times_seen'\n", + " \n", "\n", " def handle_message(self, msg):\n", " # Store message localy \n", " msg_id = msg.data\n", " # Store the message id received with the current timestamp\n", - " self.peer.store(self.strg_name, msg_id, self.peer.env.now)\n", + " if not self.peer.get_value(self.strg_name, msg_id):\n", + " self.peer.store_value(self.strg_name, msg_id, self.peer.env.now)\n", + " self.peer.store_value(self.times_name, msg_id, 1)\n", + " else: \n", + " old_value = self.peer.get_value(self.times_name, msg_id)\n", + " new_value = old_value + 1\n", + " self.peer.store_value(self.times_name, msg_id, new_value)\n", " if msg.ttl > 0:\n", " # Rely message further, modify the message\n", " exclude_peers = {msg.sender} | self.exclude_peers\n", @@ -183,7 +189,7 @@ " \n", " # Let's add a storage layer to store messages\n", " self.strg_name = 'msg_time'\n", - " self.peer.add_storage(self.strg_name, Storage())\n", + " self.times_name = 'times_seen'\n", "\n", "\n", " def produce_transaction(self):\n", @@ -193,7 +199,8 @@ " self.init_ttl), \n", " self.init_fanout)\n", " # Locally store the message counter \n", - " self.peer.store(self.strg_name, str(self.counter), self.peer.env.now)\n", + " self.peer.store_value(self.strg_name, str(self.counter), self.peer.env.now)\n", + " self.peer.store_value(self.times_name, str(self.counter), 1)\n", " self.counter+=1\n", " \n", "\n", @@ -336,9 +343,9 @@ "outputs": [], "source": [ "from time import time\n", - "from p2psimpy.simulation import BaseSimulation\n", + "from p2psimpy.simulation import Simulation\n", "\n", - "net_sim = BaseSimulation(Locations, G, prepare_peer_types())" + "net_sim = Simulation(Locations, G, prepare_peer_types())" ] }, { @@ -389,12 +396,12 @@ "outputs": [], "source": [ "client_id = 26\n", - "print(\"Time when message was produced by the client: \\n\", net_sim.peers[client_id].storage['msg_time'].txs)\n", + "print(\"Time when message was produced by the client: \\n\", net_sim.peers[client_id].storage['msg_time'].keys())\n", "\n", "# Put any peer id you want to inspect\n", "peer_id = 1 \n", "\n", - "print(\"Time when the message was received by the peer: \\n\", net_sim.peers[peer_id].storage['msg_time'].txs)" + "print(\"Time when the message was received by the peer: \\n\", net_sim.peers[peer_id].storage['msg_time'].keys())" ] }, { @@ -411,11 +418,11 @@ "# Calculate the delay \n", "\n", "def total_delay(sim, peer_id, storage_name):\n", - " store = sim.peers[peer_id].storage[storage_name].txs\n", - " for k, peer_time in store.items():\n", + " store = sim.peers[peer_id].storage[storage_name].items()\n", + " for k, peer_time in store:\n", " msg_num, client_id = k.split('_')\n", - " client_time = sim.peers[int(client_id)].storage[storage_name].txs[msg_num]\n", - " yield (int(msg_num), peer_time - client_time)\n", + " client_time = sim.peers[int(client_id)].storage[storage_name][msg_num]\n", + " yield (int(msg_num), peer_time - float(client_time))\n", " \n", "dict(total_delay(net_sim, peer_id, 'msg_time'))" ] @@ -492,11 +499,11 @@ "import pandas as pd\n", "\n", "def total_delay(sim, peer_id, storage_name):\n", - " store = sim.peers[peer_id].storage[storage_name].txs\n", - " for k, peer_time in store.items():\n", + " store = sim.peers[peer_id].storage[storage_name].items()\n", + " for k, peer_time in store:\n", " msg_num, client_id = k.split('_')\n", - " client_time = sim.peers[int(client_id)].storage[storage_name].txs[msg_num]\n", - " yield (int(msg_num), peer_time - client_time)\n", + " client_time = sim.peers[int(client_id)].storage[storage_name][msg_num]\n", + " yield (int(msg_num), peer_time - float(client_time))\n", " \n", "def get_gossip_table(sim, storage_name):\n", " return pd.DataFrame({k: dict(total_delay(sim, k, storage_name)) \n", @@ -565,9 +572,7 @@ "outputs": [], "source": [ "def show_avg_time(df):\n", - " plt.figu\n", - "\n", - "re(figsize=(10, 5))\n", + " plt.figure(figsize=(10, 5))\n", " ax = sns.boxplot(data=df, )\n", " ax.set_title('Average time to arrival', fontsize= 20 )\n", " ax.set_ylabel('Time (ms)', fontsize=12)\n", @@ -625,6 +630,22 @@ "Each gossip creates an overhead for peers. \n" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "net_sim.peers[peer_id].storage['msg_time']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, @@ -632,8 +653,8 @@ "outputs": [], "source": [ "def calc_overhead(sim, peer_id, storage_name):\n", - " store = sim.peers[peer_id].storage[storage_name].times_seen\n", - " for k, times in store.items():\n", + " store = sim.peers[peer_id].storage[storage_name].items()\n", + " for k, times in store:\n", " msg_num, client_id = k.split('_')\n", " yield (int(msg_num), int(times))\n", " \n", @@ -641,7 +662,7 @@ " return pd.DataFrame({k: dict(calc_overhead(sim, k, storage_name)) \n", " for k in sim.types_peers['peer']}).sort_index()\n", "\n", - "oh = get_overhead_table(net_sim, 'msg_time')\n", + "oh = get_overhead_table(net_sim, 'times_seen')\n", "show_heat_map(oh) " ] }, @@ -654,7 +675,7 @@ "# Show average overhead on message\n", "def show_overhead_hist(overhead_table):\n", " plt.figure(figsize=(10, 6))\n", - " sns.distplot(overhead_table, norm_hist=True)\n", + " sns.histplot(overhead_table)\n", " plt.xlabel('Overhead (# times the message is seen by the same peer)', fontsize=15)\n", "\n", "show_overhead_hist(oh)" @@ -761,7 +782,7 @@ "metadata": { "hide_input": false, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -775,7 +796,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.9" }, "toc": { "base_numbering": 1, diff --git a/03_Faults.ipynb b/03_Faults.ipynb index a5dad1f..16bb281 100644 --- a/03_Faults.ipynb +++ b/03_Faults.ipynb @@ -40,13 +40,13 @@ "from p2psimpy.services.connection_manager import BaseConnectionManager\n", "import networkx as nx\n", "\n", - "from p2psimpy.simulation import BaseSimulation\n", + "from p2psimpy.simulation import Simulation\n", "\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# Load the previous experiment configurations\n", - "exper = BaseSimulation.load_experiment(expr_dir='gossip_expr')\n", + "exper = Simulation.load_experiment(expr_dir='gossip_expr')\n", "\n", "Locations, topology, peer_services, service_impl = exper\n" ] @@ -208,7 +208,7 @@ " \n", "\n", "# Init Graph\n", - "sim = BaseSimulation(Locations, topology, peer_services, service_impl, seed=42)\n", + "sim = Simulation(Locations, topology, peer_services, service_impl, seed=42)\n", "G = sim.get_graph()\n", "pos = graphviz_layout(G)\n", "\n", @@ -475,7 +475,7 @@ "\n", "service_impl['GossipService'] = RangedPullGossipService\n", "\n", - "sim_pull = BaseSimulation(Locations, topology, peer_services, service_impl)\n", + "sim_pull = Simulation(Locations, topology, peer_services, service_impl)\n", "sim_pull.run(5_000)" ] }, @@ -686,7 +686,7 @@ "metadata": { "hide_input": false, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -700,7 +700,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.9" }, "toc": { "base_numbering": 1, diff --git a/04_Byzantine.ipynb b/04_Byzantine.ipynb index 262c629..6621ed2 100644 --- a/04_Byzantine.ipynb +++ b/04_Byzantine.ipynb @@ -43,12 +43,12 @@ "source": [ "# Initialize the experiment:\n", "import networkx as nx\n", - "import p2psimpy as p2p\n", + "from p2psimpy.simulation import Simulation\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# Load the previous experiment configurations\n", - "exper = p2p.BaseSimulation.load_experiment(expr_dir='crash_gossip')\n", + "exper = Simulation.load_experiment(expr_dir='crash_gossip')\n", "\n", "Locations, topology, peer_services, serv_impl = exper\n" ] @@ -225,7 +225,7 @@ "outputs": [], "source": [ "# Init Graph\n", - "sim = p2p.BaseSimulation(Locations, topology, peer_services, serv_impl)\n", + "sim = Simulation(Locations, topology, peer_services, serv_impl)\n", "sim.run(3_200)" ] }, @@ -387,7 +387,7 @@ "source": [ "# Run the simulation with a modificed message producer \n", "\n", - "sim2 = p2p.BaseSimulation(Locations, topology, peer_services, serv_impl)\n", + "sim2 = Simulation(Locations, topology, peer_services, serv_impl)\n", "sim2.run(3_200)\n" ] }, diff --git a/05_Consensus.ipynb b/05_Consensus.ipynb index 9c2b458..936a335 100644 --- a/05_Consensus.ipynb +++ b/05_Consensus.ipynb @@ -27,12 +27,12 @@ "source": [ "# Initialize the experiment:\n", "import networkx as nx\n", - "import p2psimpy as p2p\n", + "from p2psimpy.simulation import Simulation\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# Load the previous experiment configurations\n", - "exper = p2p.BaseSimulation.load_experiment(expr_dir='crash_gossip')\n", + "exper = Simulation.load_experiment(expr_dir='crash_gossip')\n", "\n", "Locations, topology, peer_services, serv_impl = exper\n" ] @@ -166,7 +166,7 @@ "outputs": [], "source": [ "# Init Graph\n", - "sim = p2p.BaseSimulation(Locations, topology, peer_services, serv_impl)\n", + "sim = Simulation(Locations, topology, peer_services, serv_impl)\n", "sim.run(5_200)" ] }, @@ -313,7 +313,7 @@ "metadata": { "hide_input": false, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -327,7 +327,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.9" }, "toc": { "base_numbering": 1, diff --git a/binder/environment.yml b/binder/environment.yml index 13dc568..cccd8a4 100644 --- a/binder/environment.yml +++ b/binder/environment.yml @@ -17,4 +17,4 @@ dependencies: - pydot - seaborn - pip: - - simpy \ No newline at end of file + - p2psimpy \ No newline at end of file diff --git a/p2psimpy/__init__.py b/p2psimpy/__init__.py deleted file mode 100644 index e7f0cf8..0000000 --- a/p2psimpy/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from p2psimpy.config import * -from p2psimpy.services import * -from p2psimpy.simulation import * -from p2psimpy.storage import * - -from p2psimpy.messages import * -from p2psimpy.services import * \ No newline at end of file diff --git a/p2psimpy/config.py b/p2psimpy/config.py deleted file mode 100644 index 0803f43..0000000 --- a/p2psimpy/config.py +++ /dev/null @@ -1,173 +0,0 @@ -import inspect -from ast import literal_eval as make_tuple -from collections import namedtuple -from random import choices - -import scipy.stats -import yaml - -PeerType = namedtuple('PeerType', ('config', 'service_map'), defaults=(None, {})) - - -class Dist(object): - """Wrapper on scipy.stats functios. - Able to generate a value distribution - """ - - def __init__(self, name: str, params): - self.name = name - self.params = params - - def to_repr(self): - return {self.__class__.__name__: {'name': self.name, 'params': str(self.params)}} - - def __str__(self): - return self.__class__.__name__ + ": "+str(self.name)+ str(self.params) - - def __repr__(self): - return self.__class__.__name__ + ": "+str(self.name)+ str(self.params) - - @classmethod - def from_repr(cls, yaml_dict): - return cls(**yaml_dict) - - def generate(self, n=1): - """ - Generate 'n' random values with given distribution - """ - if self.name == 'sample': - weights = self.params['weights'] if 'weights' in self.params else None - values = self.params['values'] if 'values' in self.params \ - else self.params - weights = make_tuple(weights) if type(weights) == str else weights - values = make_tuple(values) if type(values) == str else values - res = choices(values, weights=weights, k=n) - return res if n != 1 else res[0] - - dist = getattr(scipy.stats, self.name) - param = make_tuple(self.params) if type(self.params) == str else self.params - res = dist.rvs(*param[:-2], loc=param[-2], scale=param[-1], size=n) - return res if n != 1 else res[0] - - def get(self): - return self.generate(1) - -class Func(object): - - def __init__(self, func): - self.func = func - - def to_repr(self): - return self.func - - def __str__(self): - return repr(self.to_repr()) - - def __repr__(self): - return repr(self.to_repr()) - - @classmethod - def from_repr(cls, yaml_dict): - return cls(yaml_dict) - - def get(self): - return self.func - -class DistAttr(Dist): - - def get(self): - return Dist(self.name, self.params) - -class Config: - @classmethod - def save_to_yaml(cls, yaml_file): - with open(yaml_file, 'w') as s: - yaml.dump(cls.repr(), s) - - @classmethod - def _serialize(cls, val): - if isinstance(val, (tuple, list, dict)): - if type(val) == dict: - return {cls._serialize(k): cls._serialize(v) for k, v in val.items()} - else: - return list(cls._serialize(k) for k in val) - elif isinstance(val, Dist) or isinstance(val, Func): - return val.to_repr() - else: - return val - - @classmethod - def _deserialize(cls, val): - if type(val) == dict: - if 'Dist' in val: - return Dist(**val['Dist']) - elif 'DistAttr' in val: - return DistAttr(**val['DistAttr']) - else: - return {k: cls._deserialize(v) for k, v in val.items()} - else: - return val - - @classmethod - def get_attr_repr(cls): - for i in inspect.getmembers(cls): - if not i[0].startswith('_') and not callable(i[1]): - yield cls._serialize(i) - - @classmethod - def repr(cls): - root = dict() - root[str(cls.__name__)] = dict() - main = root[cls.__name__] - v = cls.get_attr_repr() - main.update(cls.get_attr_repr()) - return root - - def __str__(self): - return str(self.repr()) - - def __repr__(self): - return str(self.repr()) - - - @classmethod - def _get(cls, val): - if type(val) == dict: - return {k: cls._get(v) for k, v in val.items()} - elif isinstance(val, list): - return [cls._get(v) for v in val] - if isinstance(val, Dist) or isinstance(val, Func): - return val.get() - else: - return val - - @classmethod - def get(cls): - full_dict = dict() - for i in inspect.getmembers(cls): - if not i[0].startswith('_') and not callable(i[1]): - full_dict[i[0]] = cls._get(i[1]) - return full_dict - - @classmethod - def from_repr(cls, cls_name, yaml_dict): - cls.__name__ = cls_name - cls.__qualname__ = cls_name - val = cls._deserialize(yaml_dict) - for k, v in val.items(): - setattr(cls, k, v) - - -def load_config_from_yaml(yaml_file): - with open(yaml_file) as s: - raw = yaml.safe_load(s) - return load_config_from_repr(raw) - -def load_config_from_repr(raw_repr): - class NewConfig(Config): pass - - cls_name = list(raw_repr.keys())[0] - NewConfig.from_repr(cls_name, raw_repr[cls_name]) - return NewConfig - - diff --git a/p2psimpy/consts.py b/p2psimpy/consts.py deleted file mode 100644 index e34f21f..0000000 --- a/p2psimpy/consts.py +++ /dev/null @@ -1,5 +0,0 @@ - -KBit = 1024 / 8 -MBit = 1024 * KBit - -TEMPERED = 'ChangedData' \ No newline at end of file diff --git a/p2psimpy/defaults.py b/p2psimpy/defaults.py deleted file mode 100644 index b38f32f..0000000 --- a/p2psimpy/defaults.py +++ /dev/null @@ -1,19 +0,0 @@ -from p2psimpy.config import * -from p2psimpy.consts import MBit - -from p2psimpy.services.connection_manager import P2PConnectionManager, BaseConnectionManager - - -class BootstrapPeerConfig(Config): - bandwidth_ul = 100 * MBit - bandwidth_dl = 100 * MBit - - -class ConnectionConfig(Config): - max_peers = 100000 - - -def get_default_bootstrap_type(locations, active_p2p=False): - BootstrapPeerConfig.location = Dist('sample', locations) - services = {P2PConnectionManager: ConnectionConfig} if active_p2p else (BaseConnectionManager,) - return PeerType(BootstrapPeerConfig, services) diff --git a/p2psimpy/logger.py b/p2psimpy/logger.py deleted file mode 100644 index 906071d..0000000 --- a/p2psimpy/logger.py +++ /dev/null @@ -1,19 +0,0 @@ -import logging - - -def setup_logger(name, log_file, level=None, mode='w', clean_up=False): - if clean_up: - logging.Logger.manager.loggerDict.clear() - - if not level: - level = logging.INFO - formatter = logging.Formatter('%(name)s %(levelname)s %(message)s') - - handler = logging.FileHandler(log_file, mode=mode) - handler.setFormatter(formatter) - - logger = logging.getLogger(name) - logger.setLevel(level) - logger.addHandler(handler) - - return logger diff --git a/p2psimpy/messages.py b/p2psimpy/messages.py deleted file mode 100644 index b6cf640..0000000 --- a/p2psimpy/messages.py +++ /dev/null @@ -1,93 +0,0 @@ -from collections.abc import Iterable - -class BaseMessage(object): - __slots__ = ('sender', 'data', 'pre_task', 'post_task') - base_size = 20 - - def __init__(self, sender, data=None, **kwargs): - self.sender = sender - self.data = data - self.pre_task = kwargs.pop('pre_task', None) - self.post_task = kwargs.pop('post_task', None) - - @property - def size(self): - - def _count_size(sub_msg): - if isinstance(sub_msg, BaseMessage): - return sub_msg.size - else: - return len(repr(sub_msg)) - - iter_size = 0 - if isinstance(self.data, Iterable): - # data is an iterable - go through and add to size - for sub_msg in self.data: - iter_size += _count_size(sub_msg) - if type(self.data) == dict: - for sub_msg in self.data.values(): - iter_size += _count_size(sub_msg) - else: - iter_size += len(repr(self.data)) - - return self.base_size + iter_size - - def __repr__(self): - msg_type = '%s:' % self.__class__.__name__ - data = self.data if self.data else "" - return msg_type + str(data) - - -########## Messages ############### - -class Ping(BaseMessage): - """Response to ping""" - pass - - -class Pong(BaseMessage): - """Response to pong""" - pass - - -class RequestPeers(BaseMessage): - """Request peers to connect to""" - pass - - -class PeerList(BaseMessage): - """Peer list with known peers""" - - def __init__(self, sender, peers, **kwargs): - super().__init__(sender, set(peers), **kwargs) - - def __repr__(self): - return 'PeerList' - - -class Hello(BaseMessage): - """Offer a peer to connect""" - pass - -############ Gossip Network Messages ############## - -class GossipMessage(BaseMessage): - - base_size = 250 - - def __init__(self, sender, msg_id, data, ttl, **kwargs): - super().__init__(sender, data, **kwargs) - self.ttl = ttl - self.id = msg_id - -class SyncPing(BaseMessage): - pass - -class SyncPong(BaseMessage): - pass - -class MsgRequest(BaseMessage): - pass - -class MsgResponse(BaseMessage): - pass diff --git a/p2psimpy/network.py b/p2psimpy/network.py deleted file mode 100644 index fe9a517..0000000 --- a/p2psimpy/network.py +++ /dev/null @@ -1,43 +0,0 @@ -class Connection: - - def __init__(self, sender, receiver): - """ Class to represent connection between two peers - :param locations: Map that contains the latencies between locations - """ - self.env = sender.env - self.sender = sender - self.receiver = receiver - - self.get_latency = self.sender.sim.get_latency_delay - self.start_time = self.env.now - - def __repr__(self): - return ' %r>' % (self.sender, self.receiver) - - @property - def latency(self): - return max(self.get_latency(self.sender.location, self.receiver.location), 0) - - @property - def bandwidth(self): - return min(self.sender.bandwidth_ul, self.receiver.bandwidth_dl) - - def send(self, msg, connect=False): - """ - Simulate fire and forget send. - i.e. we don't get notified if the message was not delivered - - msg: Message tuple to deliver - connect : deliver message even if not connected yet, similar to UDP - """ - - def _transfer(): - num_bytes = msg.size - sender = msg.sender - delay = num_bytes / self.sender.bandwidth_ul - delay += self.latency / 2 - yield self.env.timeout(delay) - if self.receiver.is_connected(sender) or connect: - self.receiver.msg_queue.put(msg) - - self.env.process(_transfer()) diff --git a/p2psimpy/peer.py b/p2psimpy/peer.py deleted file mode 100644 index 4325347..0000000 --- a/p2psimpy/peer.py +++ /dev/null @@ -1,249 +0,0 @@ -import os -import random - -from simpy import Store - -from p2psimpy.logger import setup_logger -from p2psimpy.messages import BaseMessage, Hello -from p2psimpy.network import Connection -from p2psimpy.services.base import BaseHandler, BaseRunner - - -class Peer: - - def __init__(self, sim, peer_id: int, peer_type: str, - location: str, bandwidth_ul: float, bandwidth_dl: float, **kwargs): - """ - Physical representation of a Peer - sim: Simulation environment - name: Info about peer type and peer id - location: Physical location of peer - bandwidth_ul: Uplink bandwidth - bandwidth_dl: Downlink bandwidth - """ - self.sim = sim - self.env = sim.env - self.peer_type = peer_type - self.peer_id = peer_id - self.name = str(peer_id) + ":" + str(peer_type) - self.location = location - self.bandwidth_ul = bandwidth_ul - self.bandwidth_dl = bandwidth_dl - self.__dict__.update(kwargs) - - peer_repr = repr(self) - # Define log file path for results of the simulation - if sim.sim_dir: - self.log_name = os.path.join(sim.sim_dir, peer_repr + ".log") - self.logger = setup_logger(peer_repr, self.log_name) - else: - self.log_name = None - self.logger = None - - # Message queue for the received messages - self.msg_queue = Store(self.env) - - # Network connections that are online - self.online = True - self.connections = dict() - - # Known peers - self.disconnect_callbacks = [] - self.last_seen = dict() - - # Peer services - self.handlers = {} # Service.handle_message(self, msg) called on message - self.mh_map = {} # Message -> Handler map - self.runners = {} # Peer service runners - self.mprt_map = {} # Message -> Pre-Receive Trigger - - # Storage - self.storage = {} - - # Monitoring services - self.bytes_load = {} # Overhead on bytes per sec - self.msg_count_load = {} # Msg per sec - - # Start peer as it is created - self.env.process(self.run()) - - def __repr__(self): - return '%s_%s' % (self.__class__.__name__, self.name) - - def __lt__(self, other): - return self.name < other.name - - def run(self): - while True: - # Receive message - msg = yield self.msg_queue.get() - num_bytes = msg.size - sender = msg.sender - delay = num_bytes / self.bandwidth_dl - yield self.env.timeout(delay) - - # Trigger pre-receive tasks if any - if msg.pre_task: - val = yield self.env.process(msg.pre_task(msg, self)) - if not msg.pre_task or val: - self.receive(msg) - # Trigger post-receive tasks if any - - # Check for connection with any particular peer object - def is_connected(self, other): - return other in self.connections - - def bootstrap_connect(self, other): - """ - Create ad-hoc connection and send Hello - other: peer object - """ - # - cnx = Connection(self, other) - cnx.send(Hello(self), connect=True) - - def connect(self, other): - """ - Add peer to the connections and repeat the same with other peer - other: peer object - """ - if not self.is_connected(other): - if self.logger: - self.logger.info("%s: Connecting to %s", self.env.now, repr(other)) - self.connections[other] = Connection(self, other) - # We create bilateral connection - if not other.is_connected(self): - other.connect(self) - - def disconnect(self, other): - """ - Disconnect with previously connected peer - other: peer object - """ - if self.is_connected(other): - if self.logger: - self.logger.warning("%s: Breaking connection with %s", self.env.now, repr(other)) - del self.connections[other] - if other.is_connected(self): - other.disconnect(self) - for cb in self.disconnect_callbacks: - cb(self, other) - - def receive(self, msg): - """ - Receive message, will trigger handlers on the message - msg: message object - """ - if self.online: - msg_sender = msg.sender - - # Monitor the overhead of the message size - now_sec = int(self.env.now / 1000) - self.bytes_load[now_sec] = self.bytes_load.get(now_sec, 0) + msg.size - self.msg_count_load[now_sec] = self.msg_count_load.get(now_sec, 0) + 1 - - # Update peer connection data - self.last_seen[msg_sender] = self.env.now - - if self.logger: - self.logger.info("%s: Received message <%s> from %s", self.env.now, repr(msg), msg_sender) - - # Find the services that should be triggered - services = set() - for msg_type in self.mh_map: - if isinstance(msg, msg_type): - services.update(self.mh_map[msg_type]) - - if not services: - if self.logger: - self.logger.error("No handler for the message %s", type(msg)) - raise Exception("No handler for the message ", type(msg), repr(self)) - else: - for service_id in services: - self.handlers[service_id].handle_message(msg) - - def send(self, receiver, msg): - """ - Send to a receiver peer in a fire-and-forget fashion. - If receiver is not connected will raise and exception - """ - # fire and forget - if self.online: - if receiver not in self.connections: - if self.logger: - self.logger.error("%s: Sending message to a not connected peer %s", - self.env.now, repr(receiver)) - raise Exception("Not connected") - if self.logger: - self.logger.info("%s: Sending message <%s> to %s", self.env.now, repr(msg), receiver) - self.connections[receiver].send(msg) - - # Get all peer connections - def _get_connections(self, exclude_bootstrap=True, except_set: set = None, except_type: set = None): - if except_set is None: - except_set = set() - if except_type is None: - except_type = set() - if exclude_bootstrap: - except_type.add('bootstrap') - conn_set = set(self.connections.keys()) - except_set - return (p for p in conn_set if p.peer_type not in except_type) - - def gossip(self, msg, f, exclude_bootstrap=True, except_peers: set = None, except_type: set = None): - """ - Send to f neighbours selected randomly - msg: Message object - f: the fanout parameter (number of peers to gossip to) - exclude_bootstrap: Exclude bootstrap from gossip - except_peers: connected peers to exclude from gossip - except_type: exclude from gossip type of peers - """ - if not self.online: - return None - gossip_set = list(self._get_connections(exclude_bootstrap, except_peers, except_type)) - selected = random.sample(gossip_set, min(f, len(gossip_set))) - - for other in selected: - self.send(other, msg) - - return selected - - def broadcast(self, msg, exclude_bootstrap=True, except_set: set = None, except_type: set = None): - """Send to all connected peers except given """ - for other in self._get_connections(exclude_bootstrap, except_set, except_type): - self.send(other, msg) - - def add_service(self, service): - """ - Add a service to the peer - """ - serv_name = type(service).__name__ - if isinstance(service, BaseHandler): - self.handlers[serv_name] = service - for m in service.messages: - if m not in self.mh_map: - self.mh_map[m] = set() - self.mh_map[m].add(serv_name) - if isinstance(service, BaseRunner): - self.runners[serv_name] = service - - # Start all peer serice runners - def start_all_runners(self): - for runner in self.runners.values(): - runner.start() - - # Get storage used by the peer - def get_storage(self, storage_name): - return self.storage.get(storage_name) - - # Store message in peer storage - def store(self, storage_name, msg_id, msg): - if storage_name not in self.storage: - if self.logger: - self.logger.error("No storage %s found", storage_name) - raise Exception("No storage {} found" % storage_name) - self.storage[storage_name].add(msg_id, msg) - - # Add new storage for the peer - def add_storage(self, storage_name, storage): - self.storage[storage_name] = storage diff --git a/p2psimpy/peer_factory.py b/p2psimpy/peer_factory.py deleted file mode 100644 index e981d87..0000000 --- a/p2psimpy/peer_factory.py +++ /dev/null @@ -1,47 +0,0 @@ -from p2psimpy.config import PeerType -from p2psimpy.peer import Peer - - -class PeerFactory: - - # Initialize peer with self - def __init__(self): - self._last = 1 - - def create_peer(self, sim, type_name: str, peer_type: PeerType, peer_id: int = None): - # Create a peer from PeerConfig - if not peer_id: - peer_id = self._last + 1 - self._last = peer_id - else: - if int(peer_id) > self._last: - self._last = int(peer_id) - - # Add peer services and config and start peer services - peer_config_gen = peer_type.config - services = peer_type.service_map.keys() if type(peer_type.service_map) == dict else peer_type.service_map - service_configs = peer_type.service_map if type(peer_type.service_map) == dict else None - - p = Peer(sim, peer_id, type_name, **peer_config_gen.get()) - - for service in services: - if service_configs and service_configs[service]: - args = service_configs[service].get() - serv = service(p, **args) - else: - serv = service(p) - p.add_service(serv) - p.start_all_runners() - return p - - def load_from_conf(self, config): - # Parse the full configuration and add services - for p_type in config: - for service_name in config[p_type]: - if service_name != 'Peer': - self.add_service_with_conf(p_type, self.CLASS_MAP[service_name], - self.CONFIG_CLASS_MAP[service_name], - config[p_type][service_name]) - # Add peer configuration - # Exception not known service - self.raw_configs[p_type]['Peer'] = config[p_type]['Peer'] diff --git a/p2psimpy/services/__init__.py b/p2psimpy/services/__init__.py deleted file mode 100644 index 51046dc..0000000 --- a/p2psimpy/services/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from p2psimpy.services.base import * -from p2psimpy.services.gossip import * -from p2psimpy.services.connection_manager import * -from p2psimpy.services.gossip import * -from p2psimpy.services.message_producer import * -from p2psimpy.services.disruption import * diff --git a/p2psimpy/services/base.py b/p2psimpy/services/base.py deleted file mode 100644 index c5c5b51..0000000 --- a/p2psimpy/services/base.py +++ /dev/null @@ -1,85 +0,0 @@ -from p2psimpy.logger import setup_logger - - -class BaseService(object): - def __init__(self, peer, **kwargs): - self.peer = peer - self.__dict__.update(kwargs) - - self.logger = peer.logger - - @property - def env(self): - return self.peer.env - - def __repr__(self): - return '<%s %s>' % (self.__class__.__name__, self.peer.name) - - -class BaseHandler(BaseService): - """ - BaseService that will trigger on a event handle_message - """ - - def handle_message(self, msg): - """this callable is added as a listener to Peer.listeners""" - raise NotImplementedError - - @property - def messages(self): - # Specify what messages will be processed by the service - raise NotImplementedError - - -class BaseRunner(BaseService): - - def start(self): - """Start service run""" - self.env.process(self.run()) - - def run(self): - """The main running function""" - raise NotImplementedError - -class ScheduledRunner(BaseRunner): - - def next(self): - raise NotImplementedError - - def run_script(self): - raise NotImplementedError - - def run(self): - while self.next(): - self.run_script() - - - -class MockHandler(BaseHandler): - """ - BaseService that will trigger on a event handle_message - """ - - def __init__(self, peer, **kwargs): - super().__init__(peer, **kwargs) - self._messages = kwargs.pop('messages', []) - - def handle_message(self, msg): - """this callable is added as a listener to Peer.listeners""" - pass - - @property - def messages(self): - # Specify what messages will be processed by the service - return self._messages - - -class MockRunner(BaseRunner): - - def start(self): - """Start service run""" - pass - - def run(self): - """The main running function""" - pass diff --git a/p2psimpy/services/connection_manager.py b/p2psimpy/services/connection_manager.py deleted file mode 100644 index af88597..0000000 --- a/p2psimpy/services/connection_manager.py +++ /dev/null @@ -1,203 +0,0 @@ -from random import sample - -from p2psimpy.messages import Hello, PeerList, Ping, RequestPeers, Pong -from p2psimpy.peer import Peer -from p2psimpy.services.base import BaseHandler, BaseRunner - - -class BaseConnectionManager(BaseHandler, BaseRunner): - def __init__(self, peer: Peer, **kwargs): - """ - Service to - - ping peers - - disconnect unresponsive peers - Create basic peer connection manager - Attributes - ping_interval: int - Trigger connection verification every ms ping_interval. Default: 500 ms - max_silence: int - Maximum time to tolerate peer being unresponsive before action. Default: 3000 ms - min_keep_time: int - Minimum time to keep connection between peers. Default: 3000 ms - """ - BaseRunner.__init__(self, peer, **kwargs) - - # Connection Manager Attributes - self.ping_interval = kwargs.pop('ping_interval', 500) - self.max_silence = kwargs.pop('max_silence', 3000) - self.min_keep_time = kwargs.pop('min_keep_time', 3000) - - self.known_peers = set() # All known peers - self.disconnected_peers = set() # Connected in past, now disconnected - - def disconnect_cb(p, other): - assert p == self.peer - self.disconnected_peers.add(other) - - self.peer.disconnect_callbacks.append(disconnect_cb) - - @property - def messages(self): - return Hello, Ping, Pong, - - def handle_message(self, msg): - """ - Respond to the arriving messages - """ - if isinstance(msg, Hello): - self.recv_hello(msg) - if isinstance(msg, Ping): - self.peer.send(msg.sender, Pong(self.peer)) - - def ping_peers(self): - """ - ping peers that are connected - """ - for other in self.peer.connections: - if self.env.now - self.peer.last_seen.get(other, 0) > self.ping_interval: - self.peer.send(other, Ping(sender=self.peer)) - - def recv_hello(self, msg): - """ - Receive introduction message - """ - other = msg.sender - if other not in self.peer.connections: - self.peer.connect(other) - self.peer.send(other, Hello(self.peer)) - - def disconnect_unresponsive_peers(self): - now = self.env.now - for other in list(self.connected_peers): - if other not in self.peer.last_seen: - self.peer.last_seen[other] = now # assume it was recently added - elif now - self.peer.last_seen[other] > self.max_silence: - if self.logger: - self.logger.warning("%s: %s not responding", self.env.now, repr(other)) - self.peer.disconnect(other) - - @property - def connected_peers(self): - return self.peer.connections.keys() - - def run(self): - while True: - self.ping_peers() - self.disconnect_unresponsive_peers() - yield self.env.timeout(self.ping_interval) - - -class P2PConnectionManager(BaseConnectionManager): - - def __init__(self, peer: Peer, **kwargs): - """ - Service to - - ping peers - - disconnect unresponsive peers - - request and manage list of known peers - - Create basic peer connection manager - Attributes - ping_interval: int - Trigger connection verification every ms ping_interval. Default: 500 ms - max_silence: int - Maximum time to tolerate peer being unresponsive before action. Default: 3000 ms - min_keep_time: int - Minimum time to keep connection between peers. Default: 3000 ms - - peer_list_number - Number of peers in a PeerList request upon requesting peers, Default: 1 - min_peers - minimum number of peers to have in connections. Default: 15 - max_peers - maximum number of peers to have in connections. Default: 25 - peer_batch_request - number of peers to request peers at the same time. Default: 5 - """ - BaseConnectionManager.__init__(self, peer, **kwargs) - - # Connection Manager Attributes - self.peer_list_number = kwargs.pop('peer_list_number', 1) - self.min_peers = kwargs.pop('min_peers', 15) - self.max_peers = kwargs.pop('max_peers', 25) - self.peer_batch_request = kwargs.pop('peer_batch_request', 5) - - @property - def messages(self): - return super().messages + (RequestPeers, PeerList,) - - def handle_message(self, msg): - """ - Respond to the arriving messages - """ - super().handle_message(msg) - if isinstance(msg, PeerList): - self.recv_peerlist(msg) - if isinstance(msg, RequestPeers): - peer_sample = sample(self.peer.connections.keys(), - min(self.peer_list_number, len(self.peer.connections.keys()))) - reply = PeerList(self.peer, peer_sample) - # self.sample_peers(self.config.peer_list_number)) - self.peer.send(msg.sender, reply) - - - def recv_peerlist(self, msg): - """ - Receive list of peers - """ - peers = msg.data - peers.discard(self.peer) # discard own peer - self.known_peers.update(peers) - - @property - def peer_candidates(self): - candidates = self.known_peers.difference(set(self.connected_peers)) - return candidates.difference(self.disconnected_peers) - - def recv_hello(self, msg): - """ - Receive introduction message - """ - other = msg.sender - if other not in self.peer.connections: - self.peer.connect(other) - self.peer.send(other, Hello(self.peer)) - self.peer.send(other, RequestPeers(self.peer)) - - def disconnect_slowest_peer(self): - """ - Try to disconnect the slowest peer - be tolerant, so that a PeerList can be sent - """ - bw = lambda other_peer: self.peer.connections[other_peer].bandwidth - if self.connected_peers: - # get worst peer (based on latency) - sorted_peers = sorted([(bw(p), p) for p in self.connected_peers - if p not in self.disconnected_peers]) - for bw, other in sorted_peers: - start_time = self.peer.connections[other].start_time - if self.env.now - start_time > self.min_keep_time: - if self.logger: - self.logger.warning("%s: %s too slow", self.env.now, repr(other)) - self.peer.disconnect(other) - self.disconnected_peers.add(other) - break - - def monitor_connections(self): - # CASE: too few peers - if len(self.connected_peers) < self.min_peers: - needed = self.min_peers - len(self.connected_peers) - if self.logger: - self.logger.warning("%s: Not enough peers, fetching more (%s, %s)", - self.env.now, len(self.connected_peers), self.min_peers) - candidates = self.peer_candidates - if len(candidates) < needed: - self.peer.gossip(RequestPeers(self.peer), self.peer_batch_request, exclude_bootstrap=False) - for other in list(candidates)[:needed]: - self.peer.bootstrap_connect(other) - - # CASE: too many peers - if len(self.connected_peers) > self.max_peers: - if self.logger: - self.logger.warning("%s: Too many peers connected (%s, %s)", - self.env.now, len(self.connected_peers), self.max_peers) - num = max(0, len(self.connected_peers) - self.max_peers) - for i in range(num): - self.disconnect_slowest_peer() - - def run(self): - while True: - self.monitor_connections() - self.ping_peers() - self.disconnect_unresponsive_peers() - yield self.env.timeout(self.ping_interval) \ No newline at end of file diff --git a/p2psimpy/services/consensus/lottery.py b/p2psimpy/services/consensus/lottery.py deleted file mode 100644 index eb393c7..0000000 --- a/p2psimpy/services/consensus/lottery.py +++ /dev/null @@ -1,158 +0,0 @@ -from p2psimpy.services import BaseRunner, BaseHandler -from p2psimpy import BaseMessage, GossipMessage, MsgResponse, Cache -from p2psimpy.storage import DagStorage, RangedStorage -from itertools import islice -from p2psimpy.utils import to_hash - -class Block(BaseMessage): - - base_size = 100 # - - def __init__(self, sender, block_id, prev_id, txs): - super().__init__(sender, data=txs) - self.id = block_id - self.prev_id = prev_id - -class Consensus(BaseRunner, BaseHandler): - """ - Consensus based on blocks and longest-chain rule. - Args: - mining_time: DistAttr, Dist or value to indicate time for a mining process. - conf_num: number of confirmation when transaction is considered final. - max_txs_per_block: maximum number of transaction per block. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.mining_time = Cache(kwargs.pop('mining_time', Dist('norm', (1500, 300)))) - self.conf_num = kwargs.pop('conf_num', 6) - self.max_txs_per_block = kwargs.pop('max_txs_per_block', 100) - - self.init_ttl = kwargs.pop('init_ttl', 3) - self.init_fanout = kwargs.pop('init_fanout', 6) - self.pre_task = kwargs.pop('pre_task', None) - self.post_task = kwargs.pop('post_task', None) - self.exclude_types = kwargs.pop('exclude_types', {'bootstrap', 'client'}) - - - # Working transaction pool - self.tx_pool = set() - - # initialize storage for the map: tx_id: number of confirmations - self.peer.add_storage('txs_conf', RangedStorage()) - self.main_strg = self.peer.storage.get('txs_conf') - - # Storage for blocks - self.peer.add_storage('blocks', DagStorage()) - self.blocks = self.peer.storage.get('blocks') - - # Add genesis block - self.blocks.add(0, 'Genesis', []) - - def _handle_gossip_msg(self, msg): - if isinstance(msg.data, Transaction): - tx_conf = self.main_strg.get(msg.data) - if not tx_conf or tx_conf <= self.conf_num: - # add to transaction pool as either new, or potentially unconfirmed - self.tx_pool.add(msg.data) - elif isinstance(msg.data, Block): - block = msg.data - # Add block to the block storage - self.blocks.add(block.id, block.prev_id, block.data) - - - def handle_message(self, msg): - if isinstance(msg, MsgResponse): - for msg_id, sub_msg in msg.data.items(): - self._handle_gossip_msg(sub_msg) - else: - self._handle_gossip_msg(msg) - - @property - def messages(self): - return GossipMessage, MsgResponse, - - def validate_chain(self, chain): - """ - Validation rules for the chain: - - Transaction in the chain are unique - Returns: - None if chain is not valid, transactions set otherwise - """ - full_tx_set = {'0': 1} - block_conf = 1 - for c,p in zip(*(islice(chain, i, None) for i in range(2))): - for tx in self.blocks.get(c).get(p)['data']: - # validate transaction: it must be unique - if tx in full_tx_set: - # Transaction was already seen in the chain! - # Double transaction detected - Invalidate the chain - return None - else: - full_tx_set[tx.data['hash']] = block_conf - block_conf+=1 - return full_tx_set - - def choose_parent_block(self): - """ - Returns: - Block id of the last block in the longest valid chain. - """ - for chain in self.blocks.get_longest_chains(): - tx_set = self.validate_chain(chain) - if tx_set: - # add tx_set to the working db - self.main_strg.clear_all() - self.main_strg.batch_add(tx_set) - return chain[0] - - def choose_transactions_from_pool(self): - to_remove = set() - new_txs = set() - for tx in self.tx_pool: - tx_conf = self.main_strg.get(tx.data['hash']) - if not tx_conf: - new_txs.add(tx) - elif tx_conf > self.conf_num: - to_remove.add(tx) - self.tx_pool-=to_remove - - # From the new transaction choose transaction based on some criteria - # Take transactions until you hit max_size - - # For now we will just limit by the number of transactions - return list(new_txs)[:self.max_txs_per_block] - - def produce_block(self, block_id, prev_id, data): - """ - Creates a block message and gossips to the neighbors - """ - # Create block message - block = Block(self.peer, block_id, prev_id, data) - # Wrap around GossipMessage - msg_id = 'b_'+str(block_id) - msg = GossipMessage(self.peer, msg_id, block, self.init_ttl, - pre_task=self.pre_task, post_task=self.post_task) - # Add block to the local chain storage - self.blocks.add(block_id, prev_id, data) - # Store msg for the further gossip - self.peer.store('msg_time', msg_id, self.peer.env.now) - self.peer.store('msg_data', msg_id, msg) - # Gossip to the network - self.peer.gossip(msg, - self.init_fanout, except_type=self.exclude_types) - - def run(self): - while True: - # Choose last id of the block - last_id = self.choose_parent_block() - # Choose unproccessed transactions from the tx_pool - new_txs = self.choose_transactions_from_pool() - # Calculate block id with these transactions (Mining Process) - yield self.env.timeout( self.mining_time()) - block_id = to_hash(str(last_id) + str(new_txs)) - if new_txs: - self.produce_block(block_id, last_id, new_txs) - - diff --git a/p2psimpy/services/disruption.py b/p2psimpy/services/disruption.py deleted file mode 100644 index 742d0db..0000000 --- a/p2psimpy/services/disruption.py +++ /dev/null @@ -1,111 +0,0 @@ -import random - -from p2psimpy.peer import Peer -from p2psimpy.services.base import BaseRunner - -from p2psimpy.config import Dist -from p2psimpy.utils import Cache - - -class BaseDisruption(BaseRunner): - """ - Disruption in the network - """ - - def __init__(self, peer: Peer): - """ - peer: Peer object - interval: tick unit to probe disruption. Default: 10 ms - mtbf: Mean time between failures. default: 10 seconds - availability: peer availability [0-1] . default: 0.9 - """ - super().__init__(peer) - - self.is_disrupted = False - - def status_change(self): - if not self.is_disrupted: - self.is_disrupted = True - self.disruption_start() - else: - self.is_disrupted = False - self.disruption_end() - - def disruption_start(self): - raise NotImplemented - - def disruption_end(self): - raise NotImplemented - -# Define intervals for scheduled disruptions -class ScheduledDisruption(BaseDisruption): - - def __init__(self, peer: Peer, schedule): - super().__init__(peer) - - self.schedule = schedule - - def run(self): - for event in self.schedule: - yield self.env.timeout(event) - self.status_change() - -# Define intervals for random disruptions -class RandomDisruption(BaseDisruption): - - def __init__(self, peer, - start_time=Dist('norm', (1000, 70)), - disruption_time=Dist('norm', (400, 150)), - disruption_intervals=Dist('norm', (600, 200))): - - super().__init__(peer) - self.start_time = Cache(start_time) - self.disruption_time = Cache(disruption_time) - self.disruption_intervals = Cache(disruption_intervals) - - def next(self): - if self.is_disrupted: - return abs(self.disruption_intervals()) - else: - return abs(self.disruption_time()) - - def run(self): - yield self.env.timeout(abs(self.start_time())) - while True: - yield self.env.timeout(self.next()) - self.status_change() - -# Define intervals for scheduled downtime -class ScheduledDowntime(ScheduledDisruption): - - def __init__(self, peer, schedule): - super().__init__(peer, schedule) - self.last_peers = set() - - def disruption_start(self): - self.last_peers = self.peer.connections.keys() - self.peer.online = False - - def disruption_end(self): - self.peer.online = True - for other in self.last_peers: - self.peer.bootstrap_connect(other) - -# Define intervals for random downtime -class RandomDowntime(RandomDisruption): - - def __init__(self, peer, - start_time=Dist('norm', (1000, 70)), - disruption_time=Dist('norm', (400, 150)), - disruption_intervals=Dist('norm', (600, 200))): - super().__init__(peer, start_time, disruption_time, disruption_intervals) - self.last_peers = set() - - def disruption_start(self): - self.last_peers = self.peer.connections.keys() - self.peer.online = False - - def disruption_end(self): - self.peer.online = True - for other in self.last_peers: - self.peer.bootstrap_connect(other) diff --git a/p2psimpy/services/gossip.py b/p2psimpy/services/gossip.py deleted file mode 100644 index a50e277..0000000 --- a/p2psimpy/services/gossip.py +++ /dev/null @@ -1,223 +0,0 @@ -from p2psimpy.services.base import BaseHandler, BaseRunner -from p2psimpy.messages import GossipMessage, SyncPing, SyncPong, MsgRequest, MsgResponse -from p2psimpy.storage.simple import Storage, RangedStorage - -from p2psimpy.utils import Cache -from p2psimpy.config import Dist - -from re import split -from copy import copy - -# Define a special message GossipMessage: Message with ttl - - -class GossipService(BaseHandler): - """ - Simple gossip service to handle gossip messages and rely them to neighbors. - """ - - def _store_message(self, msg, msg_id=None): - if not msg_id: - msg_id = msg.id - - self.peer.store('msg_time', msg_id, self.env.now) - self.peer.store('msg_data', msg_id, msg) - - def __init__(self, peer, fanout=3, exclude_peers: set = None, - exclude_types: set = None): - super().__init__(peer) - - self.fanout = fanout - if exclude_peers is None: - self.exclude_peers = set() - else: - self.exclude_peers = exclude_peers - self.exclude_types = exclude_types - - self.peer.add_storage('msg_time', Storage()) - self.peer.add_storage('msg_data', Storage()) - - # Simulate time delays - def wait(self): - yield self.env.timeout(100) - - def handle_message(self, msg): - # Store message localy - self._store_message(msg) - if msg.ttl > 0: - # Rely message further, modify the message - exclude_peers = {msg.sender} | self.exclude_peers - # Use peer gossip - it will sample self.config.fanout and exclude sender - # If you need to exclude some peers: add it to the set - self.peer.gossip(GossipMessage(self.peer, msg.id, msg.data, msg.ttl-1, - pre_task=msg.pre_task, post_task=msg.post_task), - self.fanout, except_peers=exclude_peers, except_type=self.exclude_types) - - @property - def messages(self): - return GossipMessage, - -# Message response handler -class MessageResponder(BaseHandler): - - def _form_message_response(self, msg): - response = {} - for k in msg.data: - response[k] = self.peer.get_storage('msg_data').get(k) - return response - - def handle_message(self, msg): - self.peer.send(msg.sender, MsgResponse( - self.peer, self._form_message_response(msg))) - - @property - def messages(self): - return MsgRequest, - -# Implementation of pull based gossip -class PullGossipService(MessageResponder, BaseRunner): - - def __init__(self, peer, - exclude_types: set = None, exclude_peers: set = None, - fanout=3, round_time=500, - init_timeout=Dist('norm', (200, 100))): - super().__init__(peer) - - self.fanout = fanout - if exclude_peers is None: - self.exclude_peers = set() - else: - self.exclude_peers = exclude_peers - self.exclude_types = exclude_types - - self.sync_time = round_time - self._init_stores() - self.strg = self.peer.get_storage('msg_data') - - self.ini_time = abs(Cache(init_timeout).fetch()) - - def _init_stores(self): - self.peer.add_storage('msg_time', Storage()) - self.peer.add_storage('msg_data', Storage()) - - def _get_sync_indexes(self): - """ Will return all known indexes - """ - return self.strg.get_known_tx_ids() - - def run(self): - yield self.env.timeout(self.ini_time) - while True: - # choose random peers and perioducally synchronize the data - by filling out the missing links - yield self.env.timeout(self.sync_time) - - self.peer.gossip(SyncPing(self.peer, self._get_sync_indexes()), self.fanout, - except_peers=self.exclude_peers, except_type=self.exclude_types) - - def _self_missing(self, msg): - known = self.strg.get_known_tx_ids() - - me_missing = set(msg.data) - set(known) - if len(me_missing) > 0: - # Request missing messages - self.peer.send(msg.sender, MsgRequest(self.peer, me_missing)) - - def _peer_missing(self, msg): - known = self.strg.get_known_tx_ids() - peer_missing = set(known) - set(msg.data) - if len(peer_missing) > 0: - self.peer.send(msg.sender, SyncPong(self.peer, peer_missing)) - - def _store_message(self, msg, msg_id=None): - if not msg_id: - msg_id = msg.id - - self.peer.store('msg_time', msg_id, self.peer.env.now) - self.peer.store('msg_data', msg_id, msg) - - def handle_message(self, msg): - if type(msg) == GossipMessage: - self._store_message(msg) - elif type(msg) == SyncPing: - # Send sync pong if there more known messages - self._peer_missing(msg) - self._self_missing(msg) - elif type(msg) == SyncPong: - self._self_missing(msg) - elif type(msg) == MsgRequest: - # Answer with message response - MessageResponder.handle_message(self, msg) - elif type(msg) == MsgResponse: - # Apply to the local storage - for k, v in msg.data.items(): - self._store_message(v, k) - - @property - def messages(self): - return GossipMessage, SyncPing, SyncPong, MsgRequest, MsgResponse - -# Pull based gossip for ranged indices of peers -class RangedPullGossipService(PullGossipService): - - def _init_stores(self): - self.peer.add_storage('msg_time', Storage()) - self.peer.add_storage('msg_data', RangedStorage()) - - def _get_sync_indexes(self): - # return the last index of known peers - return self.strg.get_all_last() - - def _self_missing(self, msg): - # msg contains dict of form {client_id: last} - missing = set() - for p_id, last in msg.data.items(): - self.strg.pre_add(p_id, last) - for h_id in self.strg.get_holes(p_id): - missing.add(str(p_id)+'_'+str(h_id)) - - if len(missing) > 0: - # Request missing messages - self.peer.send(msg.sender, MsgRequest(self.peer, missing)) - - def _peer_missing(self, msg): - - peer_missing = dict() - my_last = self.strg.get_all_last() - for p_id, last in my_last.items(): - if not msg.data.get(p_id) or msg.data[p_id] < last: - peer_missing[p_id] = last - - if len(peer_missing) > 0: - self.peer.send(msg.sender, SyncPong(self.peer, peer_missing)) - - def _store_message(self, msg, msg_id=None): - if msg: - super()._store_message(msg, msg_id) - -class FullGossipService(PullGossipService): - - def _init_stores(self): - super()._init_stores() - self.peer.add_storage('msg_time', Storage()) - self.peer.add_storage('msg_data', Storage()) - - def finalized(self, tx_id, version): - # Version of a transaction is finalized - # Remove from working pool - pass - - def _form_message_response(self, msg): - response = {} - for k in msg.data: - response[k] = self.peer.get_storage('msg_data').get_all_versions(k) - return response - - -class PushPullGossipService(PullGossipService, GossipService): - - def handle_message(self, msg): - - if type(msg) == GossipMessage: - GossipService.handle_message(self, msg) - else: - PullGossipService.handle_message(self, msg) diff --git a/p2psimpy/services/message_producer.py b/p2psimpy/services/message_producer.py deleted file mode 100644 index 731178f..0000000 --- a/p2psimpy/services/message_producer.py +++ /dev/null @@ -1,65 +0,0 @@ -from p2psimpy.services.base import BaseRunner -from p2psimpy.messages import GossipMessage -from p2psimpy.storage.simple import RangedStorage - -import random -import string - - -# Class that handles producing and gossip. -# msg_rate: Number of messages per second -# init_timeout: Initial timeout before first message -# init_fanout: Factor to determine how many peer connections to gossip -class MessageProducer(BaseRunner): - - def __init__(self, peer, init_timeout=1000, msg_rate=5, - init_fanout=5, init_ttl=4, pre_task=None, post_task=None): - super().__init__(peer) - # calculate tx_interval - self.init_timeout = init_timeout - self.init_fanout = init_fanout - self.init_ttl = init_ttl - - self.pre_task = pre_task - self.post_task = post_task - - self.tx_interval = 1000 / msg_rate - self.counter = 1 - - # Let's add a storage layer to store messages - self.peer.add_storage('msg_time', RangedStorage()) - self.peer.add_storage('msg_data', RangedStorage()) - - def produce_transaction(self): - # generate new data - data = ''.join(random.choices(string.ascii_uppercase, k=20)) - msg_id = '_'.join((str(self.peer.peer_id), str(self.counter))) - msg_ttl = self.init_ttl - msg = GossipMessage(self.peer, msg_id, data, msg_ttl, - pre_task=self.pre_task, post_task=self.post_task) - - self.peer.gossip(msg, - self.init_fanout) - self.peer.store('msg_time', msg_id, self.peer.env.now) - self.peer.store('msg_data', msg_id, msg) - self.counter += 1 - - def run(self): - yield self.env.timeout(self.init_timeout) - while True: - self.produce_transaction() - yield self.env.timeout(self.tx_interval) - - -class LimitedMessageProducer(MessageProducer): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.times = kwargs.pop('times', 1) - - def run(self): - yield self.env.timeout(self.init_timeout) - for _ in range(self.times): - self.produce_transaction() - yield self.env.timeout(self.tx_interval) diff --git a/p2psimpy/services/selection.py b/p2psimpy/services/selection.py deleted file mode 100644 index 3aacd31..0000000 --- a/p2psimpy/services/selection.py +++ /dev/null @@ -1,11 +0,0 @@ -class PeerSelectionStrategy(object): - - def choose_peers(self, candidates, num=1): - """This method returns num peers from candidates list of peers """ - pass - - -class SlowPeerSelectionStrategy(PeerSelectionStrategy): - - def choose_peers(self, candidates, num=1): - return candidates[:num] diff --git a/p2psimpy/simulation.py b/p2psimpy/simulation.py deleted file mode 100644 index 48ec012..0000000 --- a/p2psimpy/simulation.py +++ /dev/null @@ -1,290 +0,0 @@ -import os -import random - -from copy import deepcopy - -import networkx as nx -from simpy import Environment - -from p2psimpy.config import load_config_from_yaml, load_config_from_repr, PeerType -from p2psimpy.defaults import get_default_bootstrap_type -from p2psimpy.logger import setup_logger -from p2psimpy.peer_factory import PeerFactory -from p2psimpy.utils import Cache - - -class BaseSimulation(object): - """ - Main class to run simulation. - """ - cash_val = 50 - - def __init__(self, locations, topology, peer_types_map, - servs_impl=None, enable_logger=False, logger_dir='logs', **kwargs): - """ - Initialize simulation with known locations, topology and services. - locations: Known locations. Either a yaml file_name, or a Config class. - topology: subscribable object map: peer_id -> type, optionally also connections to other peers - peer_types_map: A map with 'type' -> PeerType objects - logger_dir: directory to store peer logs. default: './logs/' - random_seed: for reproducibility for your experiments. - """ - - if 'random_seed' in kwargs: - self.random_seed = kwargs.get('random_seed', 42) - random.seed(self.random_seed) - self.sim_time = kwargs.get('sim_time', None) - - # Setup logging directory - if enable_logger or logger_dir != 'logs': - self.sim_dir = logger_dir - if not os.path.exists(self.sim_dir): - os.mkdir(self.sim_dir) - self.sim_log_name = os.path.join(self.sim_dir, "sim.log") - self.logger = setup_logger(__name__, self.sim_log_name, clean_up=True) - else: - self.sim_dir = None - self.logger = None - - # Initialize the simulation environment - self.env = Environment() - - self._locs = locations - self._top = topology - - # Initialize locations and latencies - if type(locations) == str: - # yaml file - try to load - self._location_generator = load_config_from_yaml(locations).latencies - self.all_locations = load_config_from_yaml(locations).locations - else: - # Load location generator - self._location_generator = locations.latencies - self.all_locations = locations.locations - - # Generate location to store in cache - self.locations = Cache(self._location_generator, self.cash_val) - - # Parse topology - self.peers_types = {} - self.topology = {} - self.types_peers = {} - if type(topology) == dict: - # The map with form peer_id -> {'type': ..., other_peer ids} - self.peers = {p: None for p in topology.keys()} - for k in topology.keys(): - self.peers_types[k] = topology.get(k).get('type') - self.topology[k] = topology.get(k).get('neighbors') - elif type(topology) == nx.Graph: - # The graph with topology of peer_ids with peer attributes - self.peers_types = nx.get_node_attributes(topology, 'type') - self.topology = topology - self.peers = {p: None for p in topology.nodes()} - - # map type -> set of peers - self.types_peers = {} - for k,v in self.peers_types.items(): - self.types_peers.setdefault(v, set()).add(k) - - self._servs = dict() - - if servs_impl: - # Load services - for type_name, peer_type in peer_types_map.items(): - s_map = peer_types_map[type_name].service_map - smap_keys = s_map.keys() if type(s_map) == dict else s_map - new_map = dict() - for k in smap_keys: - if type(k) == str: - new_map[servs_impl[k]] = s_map[k] if type(s_map) == dict else None - else: - new_map[k] = s_map[k] if type(s_map) == dict else None - self._servs[type_name] = PeerType(peer_types_map[type_name].config, new_map) - else: - self._servs = deepcopy(peer_types_map) - - - self.peer_types_configs = self._servs - - self.peer_factory = PeerFactory() - # Create peers for this simulation - for p in list(self.peers.keys()): - peer_type_name = self.peers_types[p] - peer_type = self.peer_types_configs[peer_type_name] - self.peers[p] = self.peer_factory.create_peer(self, peer_type_name, peer_type, p) - - # Bootstrap connect to peers - # If there are connections => - for p in list(self.peers.keys()): - if self.topology[p]: - for c in self.topology[p]: - self.peers[p].bootstrap_connect(self.peers[c]) - else: - # Connect to bootstrap server - if 'bootstrap' not in self.types_peers: - # No bootstrap configuration is known - use default bootstrap - use_p2p = kwargs.get('bootstrap_p2p', True) - num_bootstrap = kwargs.get('num_bootstrap', 1) - self._init_default_bootstrap_servers(self.all_locations, num=num_bootstrap, - active_p2p=use_p2p) - b_s = random.choice(list(self.types_peers['bootstrap'])) - boot_peer = self.peers[b_s] - self.peers[p].bootstrap_connect(boot_peer) - - def get_latency_delay(self, origin: str, destination: str): - """ - Get latency delay according to the latency distribution - :param origin: from location - :param destination: to location - """ - return self.locations.fetch(origin, destination) - - def save_experiment(self, expr_dir='expr', include_module_classes=False): - '''Save your experiment configurations to yaml files''' - import yaml - # Save locations - if not os.path.exists(expr_dir): - os.mkdir(expr_dir) - loc_file = os.path.join(expr_dir, "locations.yaml") - top_file = os.path.join(expr_dir, 'topology.yaml') - serv_file = os.path.join(expr_dir, 'services.yaml') - - self._locs.save_to_yaml(loc_file) - if type(self._top) == dict: - # Save dict - with open(top_file, 'w') as s: - yaml.dump(self._top, s) - else: - # This is networkx file - nx.write_yaml(self._top, top_file) - - dump_serv = {} - services = dict() - for k, pt in self._servs.items(): - new_pt = pt.config.repr() - if type(pt.service_map) == dict: - serv = dict() - for sk, sc in pt.service_map.items(): - if not sc: - serv[sk.__name__] = sc - else: - serv[sk.__name__] = sc.repr() - - services[sk.__name__] = sk if include_module_classes else None - else: - serv = tuple(k.__name__ for k in pt.service_map) - if include_module_classes: - services.update({k.__name__: k for k in pt.service_map}) - else: - services.update({k.__name__: None for k in pt.service_map}) - - dump_serv[k] = PeerType(new_pt, serv) - - with open(serv_file, 'w') as s: - yaml.dump([dump_serv, services], s) - - @staticmethod - # Load the experiment - obtain peer locations and services from the saved yaml files - def load_experiment(expr_dir='expr', load_modules=False): - import yaml - loc_file = os.path.join(expr_dir, "locations.yaml") - top_file = os.path.join(expr_dir, 'topology.yaml') - serv_file = os.path.join(expr_dir, 'services.yaml') - - locs = load_config_from_yaml(loc_file) - with open(top_file) as s: - top = yaml.load(s) - - with open(serv_file) as s: - servs, services = yaml.load(s) - for sk, pt in list(servs.items()): - - peer_config = load_config_from_repr(pt.config) - if type(pt.service_map) == dict: - new_services = dict() - for k, v in pt.service_map.items(): - k = services[k] if load_modules else k - new_services[k] = load_config_from_repr(v) if v else v - else: - new_services = [services[k] for k in pt.service_map] if load_modules else pt.service_map - servs[sk] = PeerType(peer_config, new_services) - return locs, top, servs, services - - # Add peer p - def _add_peer(self, p): - self.peers[p.peer_id] = p - self.peers_types[p.peer_id] = p.peer_type - if p.peer_type not in self.types_peers: - self.types_peers[p.peer_type] = set() - self.types_peers[p.peer_type].add(p.peer_id) - - def _init_default_bootstrap_servers(self, locations, num=1, active_p2p=False): - """ - Initialize bootstrap servers: create bootstrap peers and start them immediately. - num: number of servers - """ - if self.logger: - self.logger.warning("Init default bootstrap servers") - bpt = get_default_bootstrap_type(locations, active_p2p=active_p2p) - - for i in range(num): - p = self.peer_factory.create_peer(self, 'bootstrap', bpt) - self._add_peer(p) - - # Get names of all peers connected - def get_peers_names(self, peer_type): - if peer_type not in self.peers: - return None - return (p.name for p in self.peers[peer_type]) - - def start_all_peers(self): - """ - Start all peers' runners. - """ - for t in self.peers.keys(): - for p in self.peers[t]: - p.start_all_runners() - - def get_graph(self, include_bootstrap_peers=False): - '''Get current topology of the simulation''' - G = nx.Graph() - online_map = dict() - for p in self.peers_types.keys(): - if not include_bootstrap_peers and self.peers_types[p] == 'bootstrap': - continue - G.add_node(int(p)) - peer = self.peers[p] - if peer.online: - for other, cnx in peer.connections.items(): - if other.online and (include_bootstrap_peers or other.peer_type != 'bootstrap'): - G.add_edge(int(p), int(other.peer_id), weight=cnx.bandwidth) - online_map[int(p)] = peer.online - - nx.set_node_attributes(G, self.peers_types, 'type') - nx.set_node_attributes(G, online_map, 'is_online') - return G - - # Get average bandwidh of all the peer connections - def avg_bandwidth(self): - bws = [] - for peer in self.peers.items(): - for c in peer.connections.values(): - bws.append(c.bandwidth) - return sum(bws) / len(bws) - - # Get median bandwidh of all the peer connections - def median_bandwidth(self): - bws = [] - for peer in self.peers.items(): - for c in peer.connections.values(): - bws.append(c.bandwidth) - bws.sort() - return bws[int(len(bws) / 2)] - - # Run the peer with a time limit of until - def run(self, until): - self.env.run(until) - - # Stop the peer simulation - def stop(self): - self.env.exit(0) diff --git a/p2psimpy/storage/__init__.py b/p2psimpy/storage/__init__.py deleted file mode 100644 index 34096f6..0000000 --- a/p2psimpy/storage/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from p2psimpy.storage.dag import * -from p2psimpy.storage.simple import * \ No newline at end of file diff --git a/p2psimpy/storage/dag.py b/p2psimpy/storage/dag.py deleted file mode 100644 index d062001..0000000 --- a/p2psimpy/storage/dag.py +++ /dev/null @@ -1,24 +0,0 @@ -import networkx as nx - - -class DagStorage(object): - - def __init__(self): - self.dag = nx.DiGraph() - - def add(self, msg_id, parent_id, msg): - self.dag.add_edge(msg_id, parent_id, data=msg) - - def get(self, msg_id): - if msg_id in self.dag: - return dict(self.dag[msg_id]) - else: - return None - - def get_longest_chains(self): - G1 = self.dag.copy() - path = nx.dag_longest_path(G1) - while len(path) > 0: - yield path - G1.remove_node(path[0]) - path = nx.dag_longest_path(G1) diff --git a/p2psimpy/storage/simple.py b/p2psimpy/storage/simple.py deleted file mode 100644 index beea939..0000000 --- a/p2psimpy/storage/simple.py +++ /dev/null @@ -1,69 +0,0 @@ -class Storage: - - def __init__(self): - self.txs = dict() - self.times_seen = dict() - - def add(self, tx_id, tx): - if tx_id not in self.txs: - self.txs[tx_id] = tx - self.times_seen[tx_id] = 1 - else: - self.times_seen[tx_id] += 1 - - def batch_add(self, batch_txs): - self.txs.update(batch_txs) - - def remove(self, tx_id): - self.txs.pop(tx_id) - self.times_seen.pop(tx_id) - - def clear_all(self): - self.txs.clear() - self.times_seen.clear() - - def get_known_tx_ids(self): - return self.txs.keys() - - def get(self, msg_id): - return self.txs.get(msg_id) - - def get_all_by_prefix(self, prefix): - return {k: v for k, v in self.txs.items() if str(k).startswith(prefix)} - - -class RangedStorage(Storage): - - def __init__(self): - super().__init__() - self.index = dict() - - def get_all_last(self): - return {k: self.index[k]['last'] for k in self.index} - - def get_last(self, p_id): - return self.index[p_id]['last'] if p_id in self.index else 0 - - def get_holes(self, p_id): - return self.index[p_id]['holes'] if p_id in self.index else {} - - def pre_add(self, p_id, tx_id): - if p_id not in self.index: - self.index[p_id] = {'holes': set(), 'last': 0} - - for k in range(self.index[p_id]['last'] + 1, tx_id + 1): - # Adding holes - self.index[p_id]['holes'].add(k) - - def add(self, tx_id, tx): - p_id, c = str(tx_id).split('_') - p_id, c = int(p_id), int(c) - - self.pre_add(p_id, c) - if c > self.index[p_id]['last']: - self.index[p_id]['last'] = c - - if c in self.index[p_id]['holes']: - # Fill in the hole - self.index[p_id]['holes'].remove(c) - super().add(tx_id, tx) diff --git a/p2psimpy/tests/configspec.ini b/p2psimpy/tests/configspec.ini deleted file mode 100644 index 864a865..0000000 --- a/p2psimpy/tests/configspec.ini +++ /dev/null @@ -1,5 +0,0 @@ -val1 = string -val4 = int -val3 = MockClass() -[val2] -test1 = test2 diff --git a/p2psimpy/tests/test.ini b/p2psimpy/tests/test.ini deleted file mode 100644 index 0557b3d..0000000 --- a/p2psimpy/tests/test.ini +++ /dev/null @@ -1,5 +0,0 @@ -val1 = test -val4 = 4 -val3 = MockClass() -[val2] -test1 = test2 diff --git a/p2psimpy/tests/test.yaml b/p2psimpy/tests/test.yaml deleted file mode 100644 index fa139f9..0000000 --- a/p2psimpy/tests/test.yaml +++ /dev/null @@ -1,8 +0,0 @@ -!!python/object:test_config.TestClass -v1: hello -v2: -- v1 -- 2 -v3: !!python/object:test_config.MockClass - v1: 1 - v2: 3.0 diff --git a/p2psimpy/tests/test_config.py b/p2psimpy/tests/test_config.py deleted file mode 100644 index b52a7ab..0000000 --- a/p2psimpy/tests/test_config.py +++ /dev/null @@ -1,30 +0,0 @@ -import unittest - -import yaml - -from p2psimpy.config import PeerNameGenerator, ConnectionConfig, PeerConfig, load_from_config - - -class ConfigTests(unittest.TestCase): - - def setUp(self): - # Load config dict from yaml file - with open("../input/config.yml") as s: - self.config = yaml.safe_load(s) - - def test_load_const_config(self): - conn_config = self.config["basic"]["ConnectionConfig"] - - c = load_from_config(ConnectionConfig, conn_config) - self.assertEqual(c.max_peers, conn_config['max_peers']) - self.assertEqual(c.max_silence, conn_config['max_silence']) - self.assertEqual(c.min_keep_time, conn_config['min_keep_time']) - self.assertEqual(c.min_peers, conn_config['min_peers']) - self.assertEqual(c.ping_interval, conn_config['ping_interval']) - - def test_load_peer_config(self): - peer_config = self.config["basic"]["PeerConfig"] - name_gen = PeerNameGenerator() - - c = PeerConfig.from_config(name_gen, 'basic', peer_config) - self.assertEqual(c.name, "basic_1") \ No newline at end of file diff --git a/p2psimpy/tests/test_connection.py b/p2psimpy/tests/test_connection.py deleted file mode 100644 index f831d72..0000000 --- a/p2psimpy/tests/test_connection.py +++ /dev/null @@ -1,40 +0,0 @@ -import unittest - -from p2psimpy.config import PeerConfig -from p2psimpy.network import Connection -from p2psimpy.peer import Peer -from simpy import Environment - - -def setup_latencies(): - return dict(LocA=dict(LocA={"name": "invgamma", - "parameters": "(11.104508341331055, 0.3371934865734555, 2.0258998705983737)"}, - LocB={"name": "norm", - "parameters": "(131.0275, 0.25834811785650774)"}), - LocB=dict(LocA={"name": "norm", - "parameters": "(105.4275, 0.25834811785650774)"}, - LocB={"name": "invgamma", - "parameters": "(6.4360455224301525, 0.8312748033308526, 1.086191852963273)"}) - ) - - - -class ConnectionTests(unittest.TestCase): - - def setUp(self): - # Create simulation environment - self.env = Environment() - # Put latencies between the locations - self.env.locations = setup_latencies() - - def test_connection_latency(self): - """ - Test constant latency connections. - """ - a = PeerConfig('peer_a', 'LocA', 100, 100) - b = PeerConfig('peer_b', 'LocB', 100, 100) - peerA = Peer(self.env, a) - peerB = Peer(self.env, b) - conn = Connection(peerA, peerB) - print(conn.latency) - self.assertGreater(conn.latency, 0.1) diff --git a/p2psimpy/tests/test_simulation.py b/p2psimpy/tests/test_simulation.py deleted file mode 100644 index bc3c563..0000000 --- a/p2psimpy/tests/test_simulation.py +++ /dev/null @@ -1,52 +0,0 @@ -import unittest -from random import choice - -from p2psimpy.config import Config, Dist, PeerType -from p2psimpy.consts import MBit -from p2psimpy.services.connection_manager import BaseConnectionManager - -import networkx as nx - -from p2psimpy.simulation import BaseSimulation - - -class Locations(Config): - locations = ['Ohio', 'Ireland', 'Tokyo'] - latencies = { - 'Ohio': {'Ohio': Dist('invgamma', (5.54090, 0.333305, 0.987249)), - 'Ireland': Dist('norm', (73.6995, 1.19583092197097127)), - 'Tokyo': Dist('norm', (156.00904977375566, 0.09469886668079797)) - }, - 'Ireland': {'Ireland': Dist('invgamma', (6.4360455224301525, 0.8312748033308526, 1.086191852963273)), - 'Tokyo': Dist('norm', (131.0275, 0.25834811785650774)) - }, - 'Tokyo': {'Tokyo': Dist('invgamma', (11.104508341331055, 0.3371934865734555, 2.0258998705983737))} - } - - -# Define peer -class PeerConfig(Config): - location = Dist('sample', Locations.locations) - bandwidth_ul = Dist('norm', (50 * MBit, 10 * MBit)) - bandwidth_dl = Dist('norm', (50 * MBit, 10 * MBit)) - - -def prepare_peer_types(): - return {'peer': PeerType(PeerConfig, {BaseConnectionManager: None})} - - -def prepare_topology(num_peers=25): - # Create network topology - G = nx.erdos_renyi_graph(num_peers, 0.3) - nx.relabel_nodes(G, {k: k + 1 for k in G.nodes()}, copy=False) - types_map = {k: 'peer' for k in G.nodes()} - # Assign a peer type to the peers - nx.set_node_attributes(G, types_map, 'type') - return G - - -class ConnectionTests(unittest.TestCase): - - def test_run_simulation(self): - net_sim = BaseSimulation(Locations, prepare_topology(25), prepare_peer_types(), logger_dir='test_logs') - net_sim.run(5_000) diff --git a/p2psimpy/utils.py b/p2psimpy/utils.py deleted file mode 100644 index 0a38d47..0000000 --- a/p2psimpy/utils.py +++ /dev/null @@ -1,95 +0,0 @@ -from itertools import product -from copy import deepcopy -import networkx as nx -from random import sample - - -# Fill incomplete matrix to get a symmetric matrix - used for the latency matrix -def make_symmetric(matrix): - vals = matrix.keys() - comb = product(vals, vals) - - for c in comb: - if c[0] not in matrix or c[1] not in matrix[c[0]]: - matrix[c[0]][c[1]] = matrix[c[1]][c[0]] - - -# Convert message string to hash value -def to_hash(str_msg): - return str(hex(abs(hash(str_msg)))) - -def prepare_topology(num_peers=25, num_clients=1, client_deg=5): - # Create network topology - G = nx.erdos_renyi_graph(num_peers, 0.4) - nx.relabel_nodes(G, {k: k+1 for k in G.nodes()} ,copy=False) - - for c_id in range(num_peers+1, num_clients+num_peers+1): - for p_id in sample( list(G.nodes()), min(client_deg, len(G.nodes()))): - G.add_edge(c_id, p_id) - - types_map = {k: 'peer' if k < num_peers+1 else 'client' for k in G.nodes()} - # Assign a peer type to the peers - nx.set_node_attributes(G, types_map , 'type') - return G - - -# Cache operations -class Cache: - - def __init__(self, generator, cache_num=20, symmetric=True): - self.gen = generator - self.cache = deepcopy(generator) - self.num = cache_num - self.symmetric = symmetric - - def __call__(self, *args): - return self.fetch(*args) - - def fetch(self, *args): - try: - val = self._pop(*args) - except (IndexError, AttributeError, TypeError): - generator = self._get(self.gen, *args) - - if hasattr(generator, "params"): - self._set(generator.generate(self.num), *args) - else: - self._set([generator] * self.num, *args) - val = self._pop(*args) - return val - - def _set(self, value, *args): - if self.symmetric and len(args) == 2: - if args[0] not in self.cache or args[1] not in self.cache.get(args[0]): - self.cache[args[1]][args[0]] = value - else: - self.cache[args[0]][args[1]] = value - elif len(args) == 0: - self.cache = value - else: - self.cache[args[0]] = value - - def _get(self, val, *args): - if self.symmetric and len(args) == 2: - if args[0] not in val or args[1] not in val.get(args[0]): - return val.get(args[1]).get(args[0]) - else: - return val.get(args[0]).get(args[1]) - for attr in args: - val = val.get(attr) - return val - - def _pop(self, *args): - last = None - if len(args) == 0: - last, self.cache = self.cache[-1], self.cache[:-1] - elif len(args) == 1: - last, self.cache[args[0]] = self.cache[args[0]][-1], self.cache[args[0]][:-1] - elif self.symmetric and len(args) == 2: - if args[0] not in self.cache or args[1] not in self.cache.get(args[0]): - last, self.cache[args[1]][args[0]] = self.cache[args[1]][args[0]][-1], \ - self.cache[args[1]][args[0]][:-1] - else: - last, self.cache[args[0]][args[1]] = self.cache[args[0]][args[1]][-1], \ - self.cache[args[0]][args[1]][:-1] - return last diff --git a/p2psimpy/visual.py b/p2psimpy/visual.py deleted file mode 100644 index d172b8f..0000000 --- a/p2psimpy/visual.py +++ /dev/null @@ -1,45 +0,0 @@ -import matplotlib.pyplot as plt -import networkx as nx -from matplotlib.animation import FuncAnimation -from networkx.drawing.nx_pydot import graphviz_layout - - -def visualize_peer_client_network(G): - # Draw client/ peer network using matplotlib - fig = plt.figure(figsize=(10,10)) - master_nodes = [n for (n,ty) in \ - nx.get_node_attributes(G,'type').items() if ty == 'peer'] - client_nodes = [n for (n,ty) in \ - nx.get_node_attributes(G,'type').items() if ty == 'client'] - - pos = graphviz_layout(G) - - nx.draw_networkx_nodes(G, pos, nodelist=master_nodes, \ - node_color='blue', node_shape='o', node_size=500) - nx.draw_networkx_nodes(G, pos, nodelist=client_nodes, \ - node_color='green', node_shape='^', node_size=100, label=1) - - nx.draw_networkx_labels(G, pos, labels={k:k for k in master_nodes}, font_color='w') - - nx.draw_networkx_edges(G, pos, edgelist=G.subgraph(master_nodes).edges(), width=1.5) - nx.draw_networkx_edges(G, pos, edgelist=G.edges(nbunch=client_nodes), style='dotted') - -class VisualSimulation(object): - - def __init__(self, simulation, delta=50, total_run=3000): - # interval: draws a new frame every *interval* milliseconds - self.sim = simulation - self.delta = delta - self.total_run = total_run - self.anim = FuncAnimation(fig, self.update, interval=delta, blit=False) - - def start(self): - self.anim.event_source.start() - - def update(self): - if self.sim.env.now > self.total_run: - self.anim.event_source.stop() - self.sim.run(self.env.now+self.delta) - G = self.sim.get_graph() - visualize_peer_client_network(G) - plt.show() diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..452ac23 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1763 @@ +[[package]] +name = "appnope" +version = "0.1.2" +description = "Disable App Nap on macOS >= 10.9" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "argon2-cffi" +version = "21.3.0" +description = "The secure Argon2 password hashing algorithm." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["pre-commit", "cogapp", "tomli", "coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "sphinx-notfound-page", "furo"] +docs = ["sphinx", "sphinx-notfound-page", "furo"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["pytest", "cogapp", "pre-commit", "wheel"] +tests = ["pytest"] + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "bleach" +version = "4.1.0" +description = "An easy safelist-based HTML-sanitizing tool." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +packaging = "*" +six = ">=1.9.0" +webencodings = "*" + +[[package]] +name = "cffi" +version = "1.15.0" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "debugpy" +version = "1.5.1" +description = "An implementation of the Debug Adapter Protocol for Python" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" + +[[package]] +name = "decorator" +version = "5.1.0" +description = "Decorators for Humans" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "entrypoints" +version = "0.3" +description = "Discover and load entry points from installed packages." +category = "main" +optional = false +python-versions = ">=2.7" + +[[package]] +name = "fonttools" +version = "4.28.5" +description = "Tools to manipulate font files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +all = ["fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "zopfli (>=0.1.4)", "lz4 (>=1.7.4.2)", "matplotlib", "sympy", "skia-pathops (>=0.5.0)", "brotlicffi (>=0.8.0)", "scipy", "brotli (>=1.0.1)", "munkres", "unicodedata2 (>=13.0.0)", "xattr"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["scipy", "munkres"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=13.0.0)"] +woff = ["zopfli (>=0.1.4)", "brotlicffi (>=0.8.0)", "brotli (>=1.0.1)"] + +[[package]] +name = "importlib-resources" +version = "5.4.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[[package]] +name = "ipykernel" +version = "6.6.0" +description = "IPython Kernel for Jupyter" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +debugpy = ">=1.0.0,<2.0" +ipython = ">=7.23.1" +jupyter-client = "<8.0" +matplotlib-inline = ">=0.1.0,<0.2.0" +tornado = ">=4.2,<7.0" +traitlets = ">=5.1.0,<6.0" + +[package.extras] +test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "ipyparallel"] + +[[package]] +name = "ipython" +version = "7.30.1" +description = "IPython: Productive Interactive Computing" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" +traitlets = ">=4.2" + +[package.extras] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["notebook", "ipywidgets"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] + +[[package]] +name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "ipywidgets" +version = "7.6.5" +description = "IPython HTML widgets for Jupyter" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ipykernel = ">=4.5.1" +ipython = {version = ">=4.0.0", markers = "python_version >= \"3.3\""} +ipython-genutils = ">=0.2.0,<0.3.0" +jupyterlab-widgets = {version = ">=1.0.0", markers = "python_version >= \"3.6\""} +nbformat = ">=4.2.0" +traitlets = ">=4.3.1" +widgetsnbextension = ">=3.5.0,<3.6.0" + +[package.extras] +test = ["pytest (>=3.6.0)", "pytest-cov", "mock"] + +[[package]] +name = "jedi" +version = "0.18.1" +description = "An autocompletion tool for Python that can be used for text editors." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jinja2" +version = "3.0.3" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.3.2" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=17.4.0" +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jupyter" +version = "1.0.0" +description = "Jupyter metapackage. Install all the Jupyter components in one go." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ipykernel = "*" +ipywidgets = "*" +jupyter-console = "*" +nbconvert = "*" +notebook = "*" +qtconsole = "*" + +[[package]] +name = "jupyter-client" +version = "7.1.0" +description = "Jupyter protocol implementation and client libraries" +category = "main" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +entrypoints = "*" +jupyter-core = ">=4.6.0" +nest-asyncio = ">=1.5" +python-dateutil = ">=2.1" +pyzmq = ">=13" +tornado = ">=4.1" +traitlets = "*" + +[package.extras] +doc = ["myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] +test = ["codecov", "coverage", "ipykernel", "ipython", "mock", "mypy", "pre-commit", "pytest", "pytest-asyncio", "pytest-cov", "pytest-timeout", "jedi (<0.18)"] + +[[package]] +name = "jupyter-console" +version = "6.4.0" +description = "Jupyter terminal console" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +ipykernel = "*" +ipython = "*" +jupyter-client = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" + +[package.extras] +test = ["pexpect"] + +[[package]] +name = "jupyter-core" +version = "4.9.1" +description = "Jupyter core package. A base package on which Jupyter projects rely." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = "*" + +[[package]] +name = "jupyterlab-pygments" +version = "0.1.2" +description = "Pygments theme using JupyterLab CSS variables" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pygments = ">=2.4.1,<3" + +[[package]] +name = "jupyterlab-widgets" +version = "1.0.2" +description = "A JupyterLab extension." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "kiwisolver" +version = "1.3.2" +description = "A fast implementation of the Cassowary constraint solver" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "markupsafe" +version = "2.0.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "matplotlib" +version = "3.5.1" +description = "Python plotting package" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.0.1" +numpy = ">=1.17" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.2.1" +python-dateutil = ">=2.7" +setuptools_scm = ">=4" + +[[package]] +name = "matplotlib-inline" +version = "0.1.3" +description = "Inline Matplotlib backend for Jupyter" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mistune" +version = "0.8.4" +description = "The fastest markdown parser in pure Python" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "nbclient" +version = "0.5.9" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +category = "main" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +jupyter-client = ">=6.1.5" +nbformat = ">=5.0" +nest-asyncio = "*" +traitlets = ">=4.2" + +[package.extras] +dev = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] +sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] +test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] + +[[package]] +name = "nbconvert" +version = "6.3.0" +description = "Converting Jupyter Notebooks" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +bleach = "*" +defusedxml = "*" +entrypoints = ">=0.2.2" +jinja2 = ">=2.4" +jupyter-core = "*" +jupyterlab-pygments = "*" +mistune = ">=0.8.1,<2" +nbclient = ">=0.5.0,<0.6.0" +nbformat = ">=4.4" +pandocfilters = ">=1.4.1" +pygments = ">=2.4.1" +testpath = "*" +traitlets = ">=5.0" + +[package.extras] +all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.6)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] +docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] +serve = ["tornado (>=4.0)"] +test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.6)"] +webpdf = ["pyppeteer (==0.2.6)"] + +[[package]] +name = "nbformat" +version = "5.1.3" +description = "The Jupyter Notebook format" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +ipython-genutils = "*" +jsonschema = ">=2.4,<2.5.0 || >2.5.0" +jupyter-core = "*" +traitlets = ">=4.1" + +[package.extras] +fast = ["fastjsonschema"] +test = ["check-manifest", "fastjsonschema", "testpath", "pytest", "pytest-cov"] + +[[package]] +name = "nest-asyncio" +version = "1.5.4" +description = "Patch asyncio to allow nested event loops" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "networkx" +version = "2.6.3" +description = "Python package for creating and manipulating graphs and networks" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +default = ["numpy (>=1.19)", "scipy (>=1.5,!=1.6.1)", "matplotlib (>=3.3)", "pandas (>=1.1)"] +developer = ["black (==21.5b1)", "pre-commit (>=2.12)"] +doc = ["sphinx (>=4.0,<5.0)", "pydata-sphinx-theme (>=0.6,<1.0)", "sphinx-gallery (>=0.9,<1.0)", "numpydoc (>=1.1)", "pillow (>=8.2)", "nb2plots (>=0.6)", "texext (>=0.6.6)"] +extra = ["lxml (>=4.5)", "pygraphviz (>=1.7)", "pydot (>=1.4.1)"] +test = ["pytest (>=6.2)", "pytest-cov (>=2.12)", "codecov (>=2.1)"] + +[[package]] +name = "notebook" +version = "6.4.6" +description = "A web-based notebook environment for interactive computing" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +argon2-cffi = "*" +ipykernel = "*" +ipython-genutils = "*" +jinja2 = "*" +jupyter-client = ">=5.3.4" +jupyter-core = ">=4.6.1" +nbconvert = "*" +nbformat = "*" +nest-asyncio = ">=1.5" +prometheus-client = "*" +pyzmq = ">=17" +Send2Trash = ">=1.8.0" +terminado = ">=0.8.3" +tornado = ">=6.1" +traitlets = ">=4.2.1" + +[package.extras] +docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt", "sphinx-rtd-theme", "myst-parser"] +json-logging = ["json-logging"] +test = ["pytest", "coverage", "requests", "nbval", "selenium", "pytest-cov", "requests-unixsocket"] + +[[package]] +name = "numpy" +version = "1.21.5" +description = "NumPy is the fundamental package for array computing with Python." +category = "main" +optional = false +python-versions = ">=3.7,<3.11" + +[[package]] +name = "p2psimpy" +version = "0.1.0" +description = "Event simulator for peer-to-peer experiments" +category = "main" +optional = false +python-versions = ">=3.8,<3.11" + +[package.dependencies] +networkx = ">=2.6.3,<3.0.0" +PyYAML = ">=6.0,<7.0" +scipy = ">=1.7.3,<2.0.0" +simpy = ">=4.0.1,<5.0.0" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pandas" +version = "1.3.5" +description = "Powerful data structures for data analysis, time series, and statistics" +category = "main" +optional = false +python-versions = ">=3.7.1" + +[package.dependencies] +numpy = [ + {version = ">=1.17.3", markers = "platform_machine != \"aarch64\" and platform_machine != \"arm64\" and python_version < \"3.10\""}, + {version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""}, + {version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, +] +python-dateutil = ">=2.7.3" +pytz = ">=2017.3" + +[package.extras] +test = ["hypothesis (>=3.58)", "pytest (>=6.0)", "pytest-xdist"] + +[[package]] +name = "pandocfilters" +version = "1.5.0" +description = "Utilities for writing pandoc filters in python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pillow" +version = "8.4.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "prometheus-client" +version = "0.12.0" +description = "Python client for the Prometheus monitoring system." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +twisted = ["twisted"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.24" +description = "Library for building powerful interactive command lines in Python" +category = "main" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pygments" +version = "2.10.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "pyparsing" +version = "3.0.6" +description = "Python parsing module" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.18.0" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2021.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pywin32" +version = "303" +description = "Python for Window Extensions" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pywinpty" +version = "1.1.6" +description = "Pseudo terminal support for Windows from Python." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pyzmq" +version = "22.3.0" +description = "Python bindings for 0MQ" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} +py = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "qtconsole" +version = "5.2.2" +description = "Jupyter Qt console" +category = "main" +optional = false +python-versions = ">= 3.6" + +[package.dependencies] +ipykernel = ">=4.1" +ipython-genutils = "*" +jupyter-client = ">=4.1" +jupyter-core = "*" +pygments = "*" +pyzmq = ">=17.1" +qtpy = "*" +traitlets = "*" + +[package.extras] +doc = ["Sphinx (>=1.3)"] +test = ["flaky", "pytest", "pytest-qt"] + +[[package]] +name = "qtpy" +version = "2.0.0" +description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +packaging = "*" + +[package.extras] +test = ["pytest (>=6.0.0,<7.0)", "pytest-cov (>=2.11.0)"] + +[[package]] +name = "scipy" +version = "1.7.3" +description = "SciPy: Scientific Library for Python" +category = "main" +optional = false +python-versions = ">=3.7,<3.11" + +[package.dependencies] +numpy = ">=1.16.5,<1.23.0" + +[[package]] +name = "seaborn" +version = "0.11.2" +description = "seaborn: statistical data visualization" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +matplotlib = ">=2.2" +numpy = ">=1.15" +pandas = ">=0.23" +scipy = ">=1.0" + +[[package]] +name = "send2trash" +version = "1.8.0" +description = "Send file to trash natively under Mac OS X, Windows and Linux." +category = "main" +optional = false +python-versions = "*" + +[package.extras] +nativelib = ["pyobjc-framework-cocoa", "pywin32"] +objc = ["pyobjc-framework-cocoa"] +win32 = ["pywin32"] + +[[package]] +name = "setuptools-scm" +version = "6.3.2" +description = "the blessed package to manage your versions by scm tags" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +packaging = ">=20.0" +tomli = ">=1.0.0" + +[package.extras] +toml = ["setuptools (>=42)", "tomli (>=1.0.0)"] + +[[package]] +name = "simpy" +version = "4.0.1" +description = "Event discrete, process based simulation for Python." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "terminado" +version = "0.12.1" +description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +ptyprocess = {version = "*", markers = "os_name != \"nt\""} +pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""} +tornado = ">=4" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "testpath" +version = "0.5.0" +description = "Test utilities for code working with files and commands" +category = "main" +optional = false +python-versions = ">= 3.5" + +[package.extras] +test = ["pytest", "pathlib2"] + +[[package]] +name = "tomli" +version = "2.0.0" +description = "A lil' TOML parser" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tornado" +version = "6.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "main" +optional = false +python-versions = ">= 3.5" + +[[package]] +name = "traitlets" +version = "5.1.1" +description = "Traitlets Python configuration system" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "widgetsnbextension" +version = "3.5.2" +description = "IPython HTML widgets for Jupyter" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +notebook = ">=4.4.1" + +[[package]] +name = "zipp" +version = "3.6.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.8,<3.11" +content-hash = "82e76c123997c64dbad40d5c7f03ec9e3ae049c40eddff5ed2de65d127396f9b" + +[metadata.files] +appnope = [ + {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, + {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, +] +argon2-cffi = [ + {file = "argon2-cffi-21.3.0.tar.gz", hash = "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b"}, + {file = "argon2_cffi-21.3.0-py3-none-any.whl", hash = "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80"}, +] +argon2-cffi-bindings = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +backcall = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] +bleach = [ + {file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"}, + {file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"}, +] +cffi = [ + {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, + {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, + {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, + {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, + {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, + {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, + {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, + {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, + {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, + {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, + {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, + {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, + {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, + {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, + {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, + {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, + {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +cycler = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] +debugpy = [ + {file = "debugpy-1.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:70b422c63a833630c33e3f9cdbd9b6971f8c5afd452697e464339a21bbe862ba"}, + {file = "debugpy-1.5.1-cp310-cp310-win32.whl", hash = "sha256:3a457ad9c0059a21a6c7d563c1f18e924f5cf90278c722bd50ede6f56b77c7fe"}, + {file = "debugpy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:5d76a4fd028d8009c3faf1185b4b78ceb2273dd2499447664b03939e0368bb90"}, + {file = "debugpy-1.5.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:16db27b4b91991442f91d73604d32080b30de655aca9ba821b1972ea8171021b"}, + {file = "debugpy-1.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b073ad5e8d8c488fbb6a116986858bab0c9c4558f28deb8832c7a5a27405bd6"}, + {file = "debugpy-1.5.1-cp36-cp36m-win32.whl", hash = "sha256:318f81f37341e4e054b4267d39896b73cddb3612ca13b39d7eea45af65165e1d"}, + {file = "debugpy-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b5b3157372e0e0a1297a8b6b5280bcf1d35a40f436c7973771c972726d1e32d5"}, + {file = "debugpy-1.5.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:1ec3a086e14bba6c472632025b8fe5bdfbaef2afa1ebd5c6615ce6ed8d89bc67"}, + {file = "debugpy-1.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:26fbe53cca45a608679094791ce587b6e2798acd1d4777a8b303b07622e85182"}, + {file = "debugpy-1.5.1-cp37-cp37m-win32.whl", hash = "sha256:d876db8c312eeb02d85611e0f696abe66a2c1515e6405943609e725d5ff36f2a"}, + {file = "debugpy-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4404a62fb5332ea5c8c9132290eef50b3a0ba38cecacad5529e969a783bcbdd7"}, + {file = "debugpy-1.5.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f3a3dca9104aa14fd4210edcce6d9ce2b65bd9618c0b222135a40b9d6e2a9eeb"}, + {file = "debugpy-1.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2df2c373e85871086bd55271c929670cd4e1dba63e94a08d442db830646203b"}, + {file = "debugpy-1.5.1-cp38-cp38-win32.whl", hash = "sha256:82f5f9ce93af6861a0713f804e62ab390bb12a17f113153e47fea8bbb1dfbe36"}, + {file = "debugpy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:17a25ce9d7714f92fc97ef00cc06269d7c2b163094990ada30156ed31d9a5030"}, + {file = "debugpy-1.5.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:01e98c594b3e66d529e40edf314f849cd1a21f7a013298df58cd8e263bf8e184"}, + {file = "debugpy-1.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f73988422b17f071ad3c4383551ace1ba5ed810cbab5f9c362783d22d40a08dc"}, + {file = "debugpy-1.5.1-cp39-cp39-win32.whl", hash = "sha256:23df67fc56d59e386c342428a7953c2c06cc226d8525b11319153e96afb65b0c"}, + {file = "debugpy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:a2aa64f6d2ca7ded8a7e8a4e7cae3bc71866b09876b7b05cecad231779cb9156"}, + {file = "debugpy-1.5.1-py2.py3-none-any.whl", hash = "sha256:194f95dd3e84568b5489aab5689a3a2c044e8fdc06f1890b8b4f70b6b89f2778"}, + {file = "debugpy-1.5.1.zip", hash = "sha256:d2b09e91fbd1efa4f4fda121d49af89501beda50c18ed7499712c71a4bf3452e"}, +] +decorator = [ + {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"}, + {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"}, +] +defusedxml = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] +entrypoints = [ + {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, + {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, +] +fonttools = [ + {file = "fonttools-4.28.5-py3-none-any.whl", hash = "sha256:edf251d5d2cc0580d5f72de4621c338d8c66c5f61abb50cf486640f73c8194d5"}, + {file = "fonttools-4.28.5.zip", hash = "sha256:545c05d0f7903a863c2020e07b8f0a57517f2c40d940bded77076397872d14ca"}, +] +importlib-resources = [ + {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, + {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, +] +ipykernel = [ + {file = "ipykernel-6.6.0-py3-none-any.whl", hash = "sha256:82ded8919fa7f5483be2b6219c3b13380d93faab1fc49cc2cfcd10e9e24cc158"}, + {file = "ipykernel-6.6.0.tar.gz", hash = "sha256:3a227788216b43982d9ac28195949467627b0d16e6b8af9741d95dcaa8c41a89"}, +] +ipython = [ + {file = "ipython-7.30.1-py3-none-any.whl", hash = "sha256:fc60ef843e0863dd4e24ab2bb5698f071031332801ecf8d1aeb4fb622056545c"}, + {file = "ipython-7.30.1.tar.gz", hash = "sha256:cb6aef731bf708a7727ab6cde8df87f0281b1427d41e65d62d4b68934fa54e97"}, +] +ipython-genutils = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] +ipywidgets = [ + {file = "ipywidgets-7.6.5-py2.py3-none-any.whl", hash = "sha256:d258f582f915c62ea91023299603be095de19afb5ee271698f88327b9fe9bf43"}, + {file = "ipywidgets-7.6.5.tar.gz", hash = "sha256:00974f7cb4d5f8d494c19810fedb9fa9b64bffd3cda7c2be23c133a1ad3c99c5"}, +] +jedi = [ + {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, + {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, +] +jinja2 = [ + {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, + {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, +] +jsonschema = [ + {file = "jsonschema-4.3.2-py3-none-any.whl", hash = "sha256:8697a10a5a5edc922d2eb8556c7f35e814436f3ed8278ec2f65d40e9312d7c80"}, + {file = "jsonschema-4.3.2.tar.gz", hash = "sha256:cca171fb7544de15ccda236bf78d58434d769c9a2ce21d44e0d209e39eeb8876"}, +] +jupyter = [ + {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"}, + {file = "jupyter-1.0.0.tar.gz", hash = "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"}, + {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"}, +] +jupyter-client = [ + {file = "jupyter_client-7.1.0-py3-none-any.whl", hash = "sha256:64d93752d8cbfba0c1030c3335c3f0d9797cd1efac012652a14aac1653db11a3"}, + {file = "jupyter_client-7.1.0.tar.gz", hash = "sha256:a5f995a73cffb314ed262713ae6dfce53c6b8216cea9f332071b8ff44a6e1654"}, +] +jupyter-console = [ + {file = "jupyter_console-6.4.0-py3-none-any.whl", hash = "sha256:7799c4ea951e0e96ba8260575423cb323ea5a03fcf5503560fa3e15748869e27"}, + {file = "jupyter_console-6.4.0.tar.gz", hash = "sha256:242248e1685039cd8bff2c2ecb7ce6c1546eb50ee3b08519729e6e881aec19c7"}, +] +jupyter-core = [ + {file = "jupyter_core-4.9.1-py3-none-any.whl", hash = "sha256:1c091f3bbefd6f2a8782f2c1db662ca8478ac240e962ae2c66f0b87c818154ea"}, + {file = "jupyter_core-4.9.1.tar.gz", hash = "sha256:dce8a7499da5a53ae3afd5a9f4b02e5df1d57250cf48f3ad79da23b4778cd6fa"}, +] +jupyterlab-pygments = [ + {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"}, + {file = "jupyterlab_pygments-0.1.2.tar.gz", hash = "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146"}, +] +jupyterlab-widgets = [ + {file = "jupyterlab_widgets-1.0.2-py3-none-any.whl", hash = "sha256:f5d9efface8ec62941173ba1cffb2edd0ecddc801c11ae2931e30b50492eb8f7"}, + {file = "jupyterlab_widgets-1.0.2.tar.gz", hash = "sha256:7885092b2b96bf189c3a705cc3c412a4472ec5e8382d0b47219a66cccae73cfa"}, +] +kiwisolver = [ + {file = "kiwisolver-1.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1d819553730d3c2724582124aee8a03c846ec4362ded1034c16fb3ef309264e6"}, + {file = "kiwisolver-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d93a1095f83e908fc253f2fb569c2711414c0bfd451cab580466465b235b470"}, + {file = "kiwisolver-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4550a359c5157aaf8507e6820d98682872b9100ce7607f8aa070b4b8af6c298"}, + {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2210f28778c7d2ee13f3c2a20a3a22db889e75f4ec13a21072eabb5693801e84"}, + {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:82f49c5a79d3839bc8f38cb5f4bfc87e15f04cbafa5fbd12fb32c941cb529cfb"}, + {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9661a04ca3c950a8ac8c47f53cbc0b530bce1b52f516a1e87b7736fec24bfff0"}, + {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ddb500a2808c100e72c075cbb00bf32e62763c82b6a882d403f01a119e3f402"}, + {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72be6ebb4e92520b9726d7146bc9c9b277513a57a38efcf66db0620aec0097e0"}, + {file = "kiwisolver-1.3.2-cp310-cp310-win32.whl", hash = "sha256:83d2c9db5dfc537d0171e32de160461230eb14663299b7e6d18ca6dca21e4977"}, + {file = "kiwisolver-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:cba430db673c29376135e695c6e2501c44c256a81495da849e85d1793ee975ad"}, + {file = "kiwisolver-1.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4116ba9a58109ed5e4cb315bdcbff9838f3159d099ba5259c7c7fb77f8537492"}, + {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19554bd8d54cf41139f376753af1a644b63c9ca93f8f72009d50a2080f870f77"}, + {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a4cf5bbdc861987a7745aed7a536c6405256853c94abc9f3287c3fa401b174"}, + {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0007840186bacfaa0aba4466d5890334ea5938e0bb7e28078a0eb0e63b5b59d5"}, + {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec2eba188c1906b05b9b49ae55aae4efd8150c61ba450e6721f64620c50b59eb"}, + {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3dbb3cea20b4af4f49f84cffaf45dd5f88e8594d18568e0225e6ad9dec0e7967"}, + {file = "kiwisolver-1.3.2-cp37-cp37m-win32.whl", hash = "sha256:5326ddfacbe51abf9469fe668944bc2e399181a2158cb5d45e1d40856b2a0589"}, + {file = "kiwisolver-1.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c6572c2dab23c86a14e82c245473d45b4c515314f1f859e92608dcafbd2f19b8"}, + {file = "kiwisolver-1.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b5074fb09429f2b7bc82b6fb4be8645dcbac14e592128beeff5461dcde0af09f"}, + {file = "kiwisolver-1.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:22521219ca739654a296eea6d4367703558fba16f98688bd8ce65abff36eaa84"}, + {file = "kiwisolver-1.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c358721aebd40c243894298f685a19eb0491a5c3e0b923b9f887ef1193ddf829"}, + {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ba5a1041480c6e0a8b11a9544d53562abc2d19220bfa14133e0cdd9967e97af"}, + {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44e6adf67577dbdfa2d9f06db9fbc5639afefdb5bf2b4dfec25c3a7fbc619536"}, + {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d45d1c74f88b9f41062716c727f78f2a59a5476ecbe74956fafb423c5c87a76"}, + {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70adc3658138bc77a36ce769f5f183169bc0a2906a4f61f09673f7181255ac9b"}, + {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6a5431940f28b6de123de42f0eb47b84a073ee3c3345dc109ad550a3307dd28"}, + {file = "kiwisolver-1.3.2-cp38-cp38-win32.whl", hash = "sha256:ee040a7de8d295dbd261ef2d6d3192f13e2b08ec4a954de34a6fb8ff6422e24c"}, + {file = "kiwisolver-1.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:8dc3d842fa41a33fe83d9f5c66c0cc1f28756530cd89944b63b072281e852031"}, + {file = "kiwisolver-1.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a498bcd005e8a3fedd0022bb30ee0ad92728154a8798b703f394484452550507"}, + {file = "kiwisolver-1.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80efd202108c3a4150e042b269f7c78643420cc232a0a771743bb96b742f838f"}, + {file = "kiwisolver-1.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f8eb7b6716f5b50e9c06207a14172cf2de201e41912ebe732846c02c830455b9"}, + {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f441422bb313ab25de7b3dbfd388e790eceb76ce01a18199ec4944b369017009"}, + {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:30fa008c172355c7768159983a7270cb23838c4d7db73d6c0f6b60dde0d432c6"}, + {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f8f6c8f4f1cff93ca5058d6ec5f0efda922ecb3f4c5fb76181f327decff98b8"}, + {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba677bcaff9429fd1bf01648ad0901cea56c0d068df383d5f5856d88221fe75b"}, + {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7843b1624d6ccca403a610d1277f7c28ad184c5aa88a1750c1a999754e65b439"}, + {file = "kiwisolver-1.3.2-cp39-cp39-win32.whl", hash = "sha256:e6f5eb2f53fac7d408a45fbcdeda7224b1cfff64919d0f95473420a931347ae9"}, + {file = "kiwisolver-1.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:eedd3b59190885d1ebdf6c5e0ca56828beb1949b4dfe6e5d0256a461429ac386"}, + {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:dedc71c8eb9c5096037766390172c34fb86ef048b8e8958b4e484b9e505d66bc"}, + {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bf7eb45d14fc036514c09554bf983f2a72323254912ed0c3c8e697b62c4c158f"}, + {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b65bd35f3e06a47b5c30ea99e0c2b88f72c6476eedaf8cfbc8e66adb5479dcf"}, + {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25405f88a37c5f5bcba01c6e350086d65e7465fd1caaf986333d2a045045a223"}, + {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:bcadb05c3d4794eb9eee1dddf1c24215c92fb7b55a80beae7a60530a91060560"}, + {file = "kiwisolver-1.3.2.tar.gz", hash = "sha256:fc4453705b81d03568d5b808ad8f09c77c47534f6ac2e72e733f9ca4714aa75c"}, +] +markupsafe = [ + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, +] +matplotlib = [ + {file = "matplotlib-3.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:456cc8334f6d1124e8ff856b42d2cc1c84335375a16448189999496549f7182b"}, + {file = "matplotlib-3.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8a77906dc2ef9b67407cec0bdbf08e3971141e535db888974a915be5e1e3efc6"}, + {file = "matplotlib-3.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e70ae6475cfd0fad3816dcbf6cac536dc6f100f7474be58d59fa306e6e768a4"}, + {file = "matplotlib-3.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53273c5487d1c19c3bc03b9eb82adaf8456f243b97ed79d09dded747abaf1235"}, + {file = "matplotlib-3.5.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3b6f3fd0d8ca37861c31e9a7cab71a0ef14c639b4c95654ea1dd153158bf0df"}, + {file = "matplotlib-3.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8c87cdaf06fd7b2477f68909838ff4176f105064a72ca9d24d3f2a29f73d393"}, + {file = "matplotlib-3.5.1-cp310-cp310-win32.whl", hash = "sha256:e2f28a07b4f82abb40267864ad7b3a4ed76f1b1663e81c7efc84a9b9248f672f"}, + {file = "matplotlib-3.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:d70a32ee1f8b55eed3fd4e892f0286df8cccc7e0475c11d33b5d0a148f5c7599"}, + {file = "matplotlib-3.5.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:68fa30cec89b6139dc559ed6ef226c53fd80396da1919a1b5ef672c911aaa767"}, + {file = "matplotlib-3.5.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e3484d8455af3fdb0424eae1789af61f6a79da0c80079125112fd5c1b604218"}, + {file = "matplotlib-3.5.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e293b16cf303fe82995e41700d172a58a15efc5331125d08246b520843ef21ee"}, + {file = "matplotlib-3.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e3520a274a0e054e919f5b3279ee5dbccf5311833819ccf3399dab7c83e90a25"}, + {file = "matplotlib-3.5.1-cp37-cp37m-win32.whl", hash = "sha256:2252bfac85cec7af4a67e494bfccf9080bcba8a0299701eab075f48847cca907"}, + {file = "matplotlib-3.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:abf67e05a1b7f86583f6ebd01f69b693b9c535276f4e943292e444855870a1b8"}, + {file = "matplotlib-3.5.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6c094e4bfecd2fa7f9adffd03d8abceed7157c928c2976899de282f3600f0a3d"}, + {file = "matplotlib-3.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:506b210cc6e66a0d1c2bb765d055f4f6bc2745070fb1129203b67e85bbfa5c18"}, + {file = "matplotlib-3.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b04fc29bcef04d4e2d626af28d9d892be6aba94856cb46ed52bcb219ceac8943"}, + {file = "matplotlib-3.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577ed20ec9a18d6bdedb4616f5e9e957b4c08563a9f985563a31fd5b10564d2a"}, + {file = "matplotlib-3.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e486f60db0cd1c8d68464d9484fd2a94011c1ac8593d765d0211f9daba2bd535"}, + {file = "matplotlib-3.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b71f3a7ca935fc759f2aed7cec06cfe10bc3100fadb5dbd9c435b04e557971e1"}, + {file = "matplotlib-3.5.1-cp38-cp38-win32.whl", hash = "sha256:d24e5bb8028541ce25e59390122f5e48c8506b7e35587e5135efcb6471b4ac6c"}, + {file = "matplotlib-3.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:778d398c4866d8e36ee3bf833779c940b5f57192fa0a549b3ad67bc4c822771b"}, + {file = "matplotlib-3.5.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bb1c613908f11bac270bc7494d68b1ef6e7c224b7a4204d5dacf3522a41e2bc3"}, + {file = "matplotlib-3.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:edf5e4e1d5fb22c18820e8586fb867455de3b109c309cb4fce3aaed85d9468d1"}, + {file = "matplotlib-3.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:40e0d7df05e8efe60397c69b467fc8f87a2affeb4d562fe92b72ff8937a2b511"}, + {file = "matplotlib-3.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a350ca685d9f594123f652ba796ee37219bf72c8e0fc4b471473d87121d6d34"}, + {file = "matplotlib-3.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3e66497cd990b1a130e21919b004da2f1dc112132c01ac78011a90a0f9229778"}, + {file = "matplotlib-3.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:87900c67c0f1728e6db17c6809ec05c025c6624dcf96a8020326ea15378fe8e7"}, + {file = "matplotlib-3.5.1-cp39-cp39-win32.whl", hash = "sha256:b8a4fb2a0c5afbe9604f8a91d7d0f27b1832c3e0b5e365f95a13015822b4cd65"}, + {file = "matplotlib-3.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:fe8d40c434a8e2c68d64c6d6a04e77f21791a93ff6afe0dce169597c110d3079"}, + {file = "matplotlib-3.5.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:34a1fc29f8f96e78ec57a5eff5e8d8b53d3298c3be6df61e7aa9efba26929522"}, + {file = "matplotlib-3.5.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b19a761b948e939a9e20173aaae76070025f0024fc8f7ba08bef22a5c8573afc"}, + {file = "matplotlib-3.5.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6803299cbf4665eca14428d9e886de62e24f4223ac31ab9c5d6d5339a39782c7"}, + {file = "matplotlib-3.5.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14334b9902ec776461c4b8c6516e26b450f7ebe0b3ef8703bf5cdfbbaecf774a"}, + {file = "matplotlib-3.5.1.tar.gz", hash = "sha256:b2e9810e09c3a47b73ce9cab5a72243a1258f61e7900969097a817232246ce1c"}, +] +matplotlib-inline = [ + {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, + {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, +] +mistune = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] +nbclient = [ + {file = "nbclient-0.5.9-py3-none-any.whl", hash = "sha256:8a307be4129cce5f70eb83a57c3edbe45656623c31de54e38bb6fdfbadc428b3"}, + {file = "nbclient-0.5.9.tar.gz", hash = "sha256:99e46ddafacd0b861293bf246fed8540a184adfa3aa7d641f89031ec070701e0"}, +] +nbconvert = [ + {file = "nbconvert-6.3.0-py3-none-any.whl", hash = "sha256:8f23fbeabda4a500685d788ee091bf22cf34119304314304fb39f16e2fc32f37"}, + {file = "nbconvert-6.3.0.tar.gz", hash = "sha256:5e77d6203854944520105e38f2563a813a4a3708e8563aa598928a3b5ee1081a"}, +] +nbformat = [ + {file = "nbformat-5.1.3-py3-none-any.whl", hash = "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171"}, + {file = "nbformat-5.1.3.tar.gz", hash = "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8"}, +] +nest-asyncio = [ + {file = "nest_asyncio-1.5.4-py3-none-any.whl", hash = "sha256:3fdd0d6061a2bb16f21fe8a9c6a7945be83521d81a0d15cff52e9edee50101d6"}, + {file = "nest_asyncio-1.5.4.tar.gz", hash = "sha256:f969f6013a16fadb4adcf09d11a68a4f617c6049d7af7ac2c676110169a63abd"}, +] +networkx = [ + {file = "networkx-2.6.3-py3-none-any.whl", hash = "sha256:80b6b89c77d1dfb64a4c7854981b60aeea6360ac02c6d4e4913319e0a313abef"}, + {file = "networkx-2.6.3.tar.gz", hash = "sha256:c0946ed31d71f1b732b5aaa6da5a0388a345019af232ce2f49c766e2d6795c51"}, +] +notebook = [ + {file = "notebook-6.4.6-py3-none-any.whl", hash = "sha256:5cad068fa82cd4fb98d341c052100ed50cd69fbfb4118cb9b8ab5a346ef27551"}, + {file = "notebook-6.4.6.tar.gz", hash = "sha256:7bcdf79bd1cda534735bd9830d2cbedab4ee34d8fe1df6e7b946b3aab0902ba3"}, +] +numpy = [ + {file = "numpy-1.21.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:301e408a052fdcda5cdcf03021ebafc3c6ea093021bf9d1aa47c54d48bdad166"}, + {file = "numpy-1.21.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a7e8f6216f180f3fd4efb73de5d1eaefb5f5a1ee5b645c67333033e39440e63a"}, + {file = "numpy-1.21.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc7a7d7b0ed72589fd8b8486b9b42a564f10b8762be8bd4d9df94b807af4a089"}, + {file = "numpy-1.21.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58ca1d7c8aef6e996112d0ce873ac9dfa1eaf4a1196b4ff7ff73880a09923ba7"}, + {file = "numpy-1.21.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4b2fb01f1b4ddbe2453468ea0719f4dbb1f5caa712c8b21bb3dd1480cd30d9"}, + {file = "numpy-1.21.5-cp310-cp310-win_amd64.whl", hash = "sha256:cc1b30205d138d1005adb52087ff45708febbef0e420386f58664f984ef56954"}, + {file = "numpy-1.21.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:08de8472d9f7571f9d51b27b75e827f5296295fa78817032e84464be8bb905bc"}, + {file = "numpy-1.21.5-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4fe6a006557b87b352c04596a6e3f12a57d6e5f401d804947bd3188e6b0e0e76"}, + {file = "numpy-1.21.5-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3d893b0871322eaa2f8c7072cdb552d8e2b27645b7875a70833c31e9274d4611"}, + {file = "numpy-1.21.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341dddcfe3b7b6427a28a27baa59af5ad51baa59bfec3264f1ab287aa3b30b13"}, + {file = "numpy-1.21.5-cp37-cp37m-win32.whl", hash = "sha256:ca9c23848292c6fe0a19d212790e62f398fd9609aaa838859be8459bfbe558aa"}, + {file = "numpy-1.21.5-cp37-cp37m-win_amd64.whl", hash = "sha256:025b497014bc33fc23897859350f284323f32a2fff7654697f5a5fc2a19e9939"}, + {file = "numpy-1.21.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a5098df115340fb17fc93867317a947e1dcd978c3888c5ddb118366095851f8"}, + {file = "numpy-1.21.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:311283acf880cfcc20369201bd75da907909afc4666966c7895cbed6f9d2c640"}, + {file = "numpy-1.21.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b545ebadaa2b878c8630e5bcdb97fc4096e779f335fc0f943547c1c91540c815"}, + {file = "numpy-1.21.5-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c5562bcc1a9b61960fc8950ade44d00e3de28f891af0acc96307c73613d18f6e"}, + {file = "numpy-1.21.5-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eed2afaa97ec33b4411995be12f8bdb95c87984eaa28d76cf628970c8a2d689a"}, + {file = "numpy-1.21.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61bada43d494515d5b122f4532af226fdb5ee08fe5b5918b111279843dc6836a"}, + {file = "numpy-1.21.5-cp38-cp38-win32.whl", hash = "sha256:7b9d6b14fc9a4864b08d1ba57d732b248f0e482c7b2ff55c313137e3ed4d8449"}, + {file = "numpy-1.21.5-cp38-cp38-win_amd64.whl", hash = "sha256:dbce7adeb66b895c6aaa1fad796aaefc299ced597f6fbd9ceddb0dd735245354"}, + {file = "numpy-1.21.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:507c05c7a37b3683eb08a3ff993bd1ee1e6c752f77c2f275260533b265ecdb6c"}, + {file = "numpy-1.21.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:00c9fa73a6989895b8815d98300a20ac993c49ac36c8277e8ffeaa3631c0dbbb"}, + {file = "numpy-1.21.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69a5a8d71c308d7ef33ef72371c2388a90e3495dbb7993430e674006f94797d5"}, + {file = "numpy-1.21.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2d8adfca843bc46ac199a4645233f13abf2011a0b2f4affc5c37cd552626f27b"}, + {file = "numpy-1.21.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c293d3c0321996cd8ffe84215ffe5d269fd9d1d12c6f4ffe2b597a7c30d3e593"}, + {file = "numpy-1.21.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c978544be9e04ed12016dd295a74283773149b48f507d69b36f91aa90a643e5"}, + {file = "numpy-1.21.5-cp39-cp39-win32.whl", hash = "sha256:2a9add27d7fc0fdb572abc3b2486eb3b1395da71e0254c5552b2aad2a18b5441"}, + {file = "numpy-1.21.5-cp39-cp39-win_amd64.whl", hash = "sha256:1964db2d4a00348b7a60ee9d013c8cb0c566644a589eaa80995126eac3b99ced"}, + {file = "numpy-1.21.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a7c4b701ca418cd39e28ec3b496e6388fe06de83f5f0cb74794fa31cfa384c02"}, + {file = "numpy-1.21.5.zip", hash = "sha256:6a5928bc6241264dce5ed509e66f33676fc97f464e7a919edc672fb5532221ee"}, +] +p2psimpy = [ + {file = "p2psimpy-0.1.0-py3-none-any.whl", hash = "sha256:87d1c1084a4c6ec6f78a0181eb66589e3083530291d87ae400515feb04ef0a19"}, + {file = "p2psimpy-0.1.0.tar.gz", hash = "sha256:39961fd688f0f712578db3dcfd8de7325c3b1f6708489e3542696436a466cba4"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pandas = [ + {file = "pandas-1.3.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:62d5b5ce965bae78f12c1c0df0d387899dd4211ec0bdc52822373f13a3a022b9"}, + {file = "pandas-1.3.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:adfeb11be2d54f275142c8ba9bf67acee771b7186a5745249c7d5a06c670136b"}, + {file = "pandas-1.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:60a8c055d58873ad81cae290d974d13dd479b82cbb975c3e1fa2cf1920715296"}, + {file = "pandas-1.3.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd541ab09e1f80a2a1760032d665f6e032d8e44055d602d65eeea6e6e85498cb"}, + {file = "pandas-1.3.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2651d75b9a167cc8cc572cf787ab512d16e316ae00ba81874b560586fa1325e0"}, + {file = "pandas-1.3.5-cp310-cp310-win_amd64.whl", hash = "sha256:aaf183a615ad790801fa3cf2fa450e5b6d23a54684fe386f7e3208f8b9bfbef6"}, + {file = "pandas-1.3.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:344295811e67f8200de2390093aeb3c8309f5648951b684d8db7eee7d1c81fb7"}, + {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:552020bf83b7f9033b57cbae65589c01e7ef1544416122da0c79140c93288f56"}, + {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cce0c6bbeb266b0e39e35176ee615ce3585233092f685b6a82362523e59e5b4"}, + {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d28a3c65463fd0d0ba8bbb7696b23073efee0510783340a44b08f5e96ffce0c"}, + {file = "pandas-1.3.5-cp37-cp37m-win32.whl", hash = "sha256:a62949c626dd0ef7de11de34b44c6475db76995c2064e2d99c6498c3dba7fe58"}, + {file = "pandas-1.3.5-cp37-cp37m-win_amd64.whl", hash = "sha256:8025750767e138320b15ca16d70d5cdc1886e8f9cc56652d89735c016cd8aea6"}, + {file = "pandas-1.3.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fe95bae4e2d579812865db2212bb733144e34d0c6785c0685329e5b60fcb85dd"}, + {file = "pandas-1.3.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f261553a1e9c65b7a310302b9dbac31cf0049a51695c14ebe04e4bfd4a96f02"}, + {file = "pandas-1.3.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b6dbec5f3e6d5dc80dcfee250e0a2a652b3f28663492f7dab9a24416a48ac39"}, + {file = "pandas-1.3.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3bc49af96cd6285030a64779de5b3688633a07eb75c124b0747134a63f4c05f"}, + {file = "pandas-1.3.5-cp38-cp38-win32.whl", hash = "sha256:b6b87b2fb39e6383ca28e2829cddef1d9fc9e27e55ad91ca9c435572cdba51bf"}, + {file = "pandas-1.3.5-cp38-cp38-win_amd64.whl", hash = "sha256:a395692046fd8ce1edb4c6295c35184ae0c2bbe787ecbe384251da609e27edcb"}, + {file = "pandas-1.3.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd971a3f08b745a75a86c00b97f3007c2ea175951286cdda6abe543e687e5f2f"}, + {file = "pandas-1.3.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37f06b59e5bc05711a518aa10beaec10942188dccb48918bb5ae602ccbc9f1a0"}, + {file = "pandas-1.3.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c21778a688d3712d35710501f8001cdbf96eb70a7c587a3d5613573299fdca6"}, + {file = "pandas-1.3.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3345343206546545bc26a05b4602b6a24385b5ec7c75cb6059599e3d56831da2"}, + {file = "pandas-1.3.5-cp39-cp39-win32.whl", hash = "sha256:c69406a2808ba6cf580c2255bcf260b3f214d2664a3a4197d0e640f573b46fd3"}, + {file = "pandas-1.3.5-cp39-cp39-win_amd64.whl", hash = "sha256:32e1a26d5ade11b547721a72f9bfc4bd113396947606e00d5b4a5b79b3dcb006"}, + {file = "pandas-1.3.5.tar.gz", hash = "sha256:1e4285f5de1012de20ca46b188ccf33521bff61ba5c5ebd78b4fb28e5416a9f1"}, +] +pandocfilters = [ + {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"}, + {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"}, +] +parso = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +pickleshare = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] +pillow = [ + {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"}, + {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"}, + {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"}, + {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649"}, + {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f"}, + {file = "Pillow-8.4.0-cp310-cp310-win32.whl", hash = "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a"}, + {file = "Pillow-8.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39"}, + {file = "Pillow-8.4.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55"}, + {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c"}, + {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a"}, + {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645"}, + {file = "Pillow-8.4.0-cp36-cp36m-win32.whl", hash = "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9"}, + {file = "Pillow-8.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff"}, + {file = "Pillow-8.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153"}, + {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29"}, + {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8"}, + {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488"}, + {file = "Pillow-8.4.0-cp37-cp37m-win32.whl", hash = "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b"}, + {file = "Pillow-8.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b"}, + {file = "Pillow-8.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49"}, + {file = "Pillow-8.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585"}, + {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779"}, + {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409"}, + {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df"}, + {file = "Pillow-8.4.0-cp38-cp38-win32.whl", hash = "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09"}, + {file = "Pillow-8.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76"}, + {file = "Pillow-8.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a"}, + {file = "Pillow-8.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e"}, + {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b"}, + {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20"}, + {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed"}, + {file = "Pillow-8.4.0-cp39-cp39-win32.whl", hash = "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02"}, + {file = "Pillow-8.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b"}, + {file = "Pillow-8.4.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2"}, + {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad"}, + {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698"}, + {file = "Pillow-8.4.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc"}, + {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df"}, + {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b"}, + {file = "Pillow-8.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc"}, + {file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"}, +] +prometheus-client = [ + {file = "prometheus_client-0.12.0-py2.py3-none-any.whl", hash = "sha256:317453ebabff0a1b02df7f708efbab21e3489e7072b61cb6957230dd004a0af0"}, + {file = "prometheus_client-0.12.0.tar.gz", hash = "sha256:1b12ba48cee33b9b0b9de64a1047cbd3c5f2d0ab6ebcead7ddda613a750ec3c5"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.24-py3-none-any.whl", hash = "sha256:e56f2ff799bacecd3e88165b1e2f5ebf9bcd59e80e06d395fa0cc4b8bd7bb506"}, + {file = "prompt_toolkit-3.0.24.tar.gz", hash = "sha256:1bb05628c7d87b645974a1bad3f17612be0c29fa39af9f7688030163f680bad6"}, +] +ptyprocess = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pygments = [ + {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, + {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, +] +pyparsing = [ + {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, + {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, +] +pyrsistent = [ + {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-win32.whl", hash = "sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1"}, + {file = "pyrsistent-0.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-win32.whl", hash = "sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f"}, + {file = "pyrsistent-0.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2"}, + {file = "pyrsistent-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427"}, + {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef"}, + {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c"}, + {file = "pyrsistent-0.18.0-cp38-cp38-win32.whl", hash = "sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78"}, + {file = "pyrsistent-0.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b"}, + {file = "pyrsistent-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4"}, + {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680"}, + {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426"}, + {file = "pyrsistent-0.18.0-cp39-cp39-win32.whl", hash = "sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b"}, + {file = "pyrsistent-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea"}, + {file = "pyrsistent-0.18.0.tar.gz", hash = "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] +pytz = [ + {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, + {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, +] +pywin32 = [ + {file = "pywin32-303-cp310-cp310-win32.whl", hash = "sha256:6fed4af057039f309263fd3285d7b8042d41507343cd5fa781d98fcc5b90e8bb"}, + {file = "pywin32-303-cp310-cp310-win_amd64.whl", hash = "sha256:51cb52c5ec6709f96c3f26e7795b0bf169ee0d8395b2c1d7eb2c029a5008ed51"}, + {file = "pywin32-303-cp311-cp311-win32.whl", hash = "sha256:d9b5d87ca944eb3aa4cd45516203ead4b37ab06b8b777c54aedc35975dec0dee"}, + {file = "pywin32-303-cp311-cp311-win_amd64.whl", hash = "sha256:fcf44032f5b14fcda86028cdf49b6ebdaea091230eb0a757282aa656e4732439"}, + {file = "pywin32-303-cp36-cp36m-win32.whl", hash = "sha256:aad484d52ec58008ca36bd4ad14a71d7dd0a99db1a4ca71072213f63bf49c7d9"}, + {file = "pywin32-303-cp36-cp36m-win_amd64.whl", hash = "sha256:2a09632916b6bb231ba49983fe989f2f625cea237219530e81a69239cd0c4559"}, + {file = "pywin32-303-cp37-cp37m-win32.whl", hash = "sha256:b1675d82bcf6dbc96363fca747bac8bff6f6e4a447a4287ac652aa4b9adc796e"}, + {file = "pywin32-303-cp37-cp37m-win_amd64.whl", hash = "sha256:c268040769b48a13367221fced6d4232ed52f044ffafeda247bd9d2c6bdc29ca"}, + {file = "pywin32-303-cp38-cp38-win32.whl", hash = "sha256:5f9ec054f5a46a0f4dfd72af2ce1372f3d5a6e4052af20b858aa7df2df7d355b"}, + {file = "pywin32-303-cp38-cp38-win_amd64.whl", hash = "sha256:793bf74fce164bcffd9d57bb13c2c15d56e43c9542a7b9687b4fccf8f8a41aba"}, + {file = "pywin32-303-cp39-cp39-win32.whl", hash = "sha256:7d3271c98434617a11921c5ccf74615794d97b079e22ed7773790822735cc352"}, + {file = "pywin32-303-cp39-cp39-win_amd64.whl", hash = "sha256:79cbb862c11b9af19bcb682891c1b91942ec2ff7de8151e2aea2e175899cda34"}, +] +pywinpty = [ + {file = "pywinpty-1.1.6-cp310-none-win_amd64.whl", hash = "sha256:5f526f21b569b5610a61e3b6126259c76da979399598e5154498582df3736ade"}, + {file = "pywinpty-1.1.6-cp36-none-win_amd64.whl", hash = "sha256:7576e14f42b31fa98b62d24ded79754d2ea4625570c016b38eb347ce158a30f2"}, + {file = "pywinpty-1.1.6-cp37-none-win_amd64.whl", hash = "sha256:979ffdb9bdbe23db3f46fc7285fd6dbb86b80c12325a50582b211b3894072354"}, + {file = "pywinpty-1.1.6-cp38-none-win_amd64.whl", hash = "sha256:2308b1fc77545427610a705799d4ead5e7f00874af3fb148a03e202437456a7e"}, + {file = "pywinpty-1.1.6-cp39-none-win_amd64.whl", hash = "sha256:c703bf569a98ab7844b9daf37e88ab86f31862754ef6910a8b3824993a525c72"}, + {file = "pywinpty-1.1.6.tar.gz", hash = "sha256:8808f07350c709119cc4464144d6e749637f98e15acc1e5d3c37db1953d2eebc"}, +] +pyyaml = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +pyzmq = [ + {file = "pyzmq-22.3.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:6b217b8f9dfb6628f74b94bdaf9f7408708cb02167d644edca33f38746ca12dd"}, + {file = "pyzmq-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2841997a0d85b998cbafecb4183caf51fd19c4357075dfd33eb7efea57e4c149"}, + {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f89468059ebc519a7acde1ee50b779019535db8dcf9b8c162ef669257fef7a93"}, + {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea12133df25e3a6918718fbb9a510c6ee5d3fdd5a346320421aac3882f4feeea"}, + {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c532fd68b93998aab92356be280deec5de8f8fe59cd28763d2cc8a58747b7f"}, + {file = "pyzmq-22.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f907c7359ce8bf7f7e63c82f75ad0223384105f5126f313400b7e8004d9b33c3"}, + {file = "pyzmq-22.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:902319cfe23366595d3fa769b5b751e6ee6750a0a64c5d9f757d624b2ac3519e"}, + {file = "pyzmq-22.3.0-cp310-cp310-win32.whl", hash = "sha256:67db33bea0a29d03e6eeec55a8190e033318cee3cbc732ba8fd939617cbf762d"}, + {file = "pyzmq-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:7661fc1d5cb73481cf710a1418a4e1e301ed7d5d924f91c67ba84b2a1b89defd"}, + {file = "pyzmq-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79244b9e97948eaf38695f4b8e6fc63b14b78cc37f403c6642ba555517ac1268"}, + {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab888624ed68930442a3f3b0b921ad7439c51ba122dbc8c386e6487a658e4a4e"}, + {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18cd854b423fce44951c3a4d3e686bac8f1243d954f579e120a1714096637cc0"}, + {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:de8df0684398bd74ad160afdc2a118ca28384ac6f5e234eb0508858d8d2d9364"}, + {file = "pyzmq-22.3.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:62bcade20813796c426409a3e7423862d50ff0639f5a2a95be4b85b09a618666"}, + {file = "pyzmq-22.3.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ea5a79e808baef98c48c884effce05c31a0698c1057de8fc1c688891043c1ce1"}, + {file = "pyzmq-22.3.0-cp36-cp36m-win32.whl", hash = "sha256:3c1895c95be92600233e476fe283f042e71cf8f0b938aabf21b7aafa62a8dac9"}, + {file = "pyzmq-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:851977788b9caa8ed011f5f643d3ee8653af02c5fc723fa350db5125abf2be7b"}, + {file = "pyzmq-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b4ebed0977f92320f6686c96e9e8dd29eed199eb8d066936bac991afc37cbb70"}, + {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42abddebe2c6a35180ca549fadc7228d23c1e1f76167c5ebc8a936b5804ea2df"}, + {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1e41b32d6f7f9c26bc731a8b529ff592f31fc8b6ef2be9fa74abd05c8a342d7"}, + {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:be4e0f229cf3a71f9ecd633566bd6f80d9fa6afaaff5489492be63fe459ef98c"}, + {file = "pyzmq-22.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08c4e315a76ef26eb833511ebf3fa87d182152adf43dedee8d79f998a2162a0b"}, + {file = "pyzmq-22.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:badb868fff14cfd0e200eaa845887b1011146a7d26d579aaa7f966c203736b92"}, + {file = "pyzmq-22.3.0-cp37-cp37m-win32.whl", hash = "sha256:7c58f598d9fcc52772b89a92d72bf8829c12d09746a6d2c724c5b30076c1f11d"}, + {file = "pyzmq-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2b97502c16a5ec611cd52410bdfaab264997c627a46b0f98d3f666227fd1ea2d"}, + {file = "pyzmq-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d728b08448e5ac3e4d886b165385a262883c34b84a7fe1166277fe675e1c197a"}, + {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:480b9931bfb08bf8b094edd4836271d4d6b44150da051547d8c7113bf947a8b0"}, + {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7dc09198e4073e6015d9a8ea093fc348d4e59de49382476940c3dd9ae156fba8"}, + {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ca6cd58f62a2751728016d40082008d3b3412a7f28ddfb4a2f0d3c130f69e74"}, + {file = "pyzmq-22.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:468bd59a588e276961a918a3060948ae68f6ff5a7fa10bb2f9160c18fe341067"}, + {file = "pyzmq-22.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c88fa7410e9fc471e0858638f403739ee869924dd8e4ae26748496466e27ac59"}, + {file = "pyzmq-22.3.0-cp38-cp38-win32.whl", hash = "sha256:c0f84360dcca3481e8674393bdf931f9f10470988f87311b19d23cda869bb6b7"}, + {file = "pyzmq-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:f762442bab706fd874064ca218b33a1d8e40d4938e96c24dafd9b12e28017f45"}, + {file = "pyzmq-22.3.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:954e73c9cd4d6ae319f1c936ad159072b6d356a92dcbbabfd6e6204b9a79d356"}, + {file = "pyzmq-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f43b4a2e6218371dd4f41e547bd919ceeb6ebf4abf31a7a0669cd11cd91ea973"}, + {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:acebba1a23fb9d72b42471c3771b6f2f18dcd46df77482612054bd45c07dfa36"}, + {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cf98fd7a6c8aaa08dbc699ffae33fd71175696d78028281bc7b832b26f00ca57"}, + {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d072f7dfbdb184f0786d63bda26e8a0882041b1e393fbe98940395f7fab4c5e2"}, + {file = "pyzmq-22.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:53f4fd13976789ffafedd4d46f954c7bb01146121812b72b4ddca286034df966"}, + {file = "pyzmq-22.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1b5d457acbadcf8b27561deeaa386b0217f47626b29672fa7bd31deb6e91e1b"}, + {file = "pyzmq-22.3.0-cp39-cp39-win32.whl", hash = "sha256:e6a02cf7271ee94674a44f4e62aa061d2d049001c844657740e156596298b70b"}, + {file = "pyzmq-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d3dcb5548ead4f1123851a5ced467791f6986d68c656bc63bfff1bf9e36671e2"}, + {file = "pyzmq-22.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3a4c9886d61d386b2b493377d980f502186cd71d501fffdba52bd2a0880cef4f"}, + {file = "pyzmq-22.3.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:80e043a89c6cadefd3a0712f8a1322038e819ebe9dbac7eca3bce1721bcb63bf"}, + {file = "pyzmq-22.3.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1621e7a2af72cced1f6ec8ca8ca91d0f76ac236ab2e8828ac8fe909512d566cb"}, + {file = "pyzmq-22.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d6157793719de168b199194f6b6173f0ccd3bf3499e6870fac17086072e39115"}, + {file = "pyzmq-22.3.0.tar.gz", hash = "sha256:8eddc033e716f8c91c6a2112f0a8ebc5e00532b4a6ae1eb0ccc48e027f9c671c"}, +] +qtconsole = [ + {file = "qtconsole-5.2.2-py3-none-any.whl", hash = "sha256:4aa6a3e600e0c8cf16853f2378311bc2371f57cb0f22ecfc28994f4cf409ee2e"}, + {file = "qtconsole-5.2.2.tar.gz", hash = "sha256:8f9db97b27782184efd0a0f2d57ea3bd852d053747a2e442a9011329c082976d"}, +] +qtpy = [ + {file = "QtPy-2.0.0-py3-none-any.whl", hash = "sha256:74bf26be3288aadc843cf3381d5ef0b82f11417ecdcbf26718a408f32590f1ac"}, + {file = "QtPy-2.0.0.tar.gz", hash = "sha256:777e333df4d711b2ec9743117ab319dadfbd743a5a0eee35923855ca3d35cd9d"}, +] +scipy = [ + {file = "scipy-1.7.3-1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c9e04d7e9b03a8a6ac2045f7c5ef741be86727d8f49c45db45f244bdd2bcff17"}, + {file = "scipy-1.7.3-1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:b0e0aeb061a1d7dcd2ed59ea57ee56c9b23dd60100825f98238c06ee5cc4467e"}, + {file = "scipy-1.7.3-1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:b78a35c5c74d336f42f44106174b9851c783184a85a3fe3e68857259b37b9ffb"}, + {file = "scipy-1.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:173308efba2270dcd61cd45a30dfded6ec0085b4b6eb33b5eb11ab443005e088"}, + {file = "scipy-1.7.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:21b66200cf44b1c3e86495e3a436fc7a26608f92b8d43d344457c54f1c024cbc"}, + {file = "scipy-1.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceebc3c4f6a109777c0053dfa0282fddb8893eddfb0d598574acfb734a926168"}, + {file = "scipy-1.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7eaea089345a35130bc9a39b89ec1ff69c208efa97b3f8b25ea5d4c41d88094"}, + {file = "scipy-1.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:304dfaa7146cffdb75fbf6bb7c190fd7688795389ad060b970269c8576d038e9"}, + {file = "scipy-1.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:033ce76ed4e9f62923e1f8124f7e2b0800db533828c853b402c7eec6e9465d80"}, + {file = "scipy-1.7.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4d242d13206ca4302d83d8a6388c9dfce49fc48fdd3c20efad89ba12f785bf9e"}, + {file = "scipy-1.7.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8499d9dd1459dc0d0fe68db0832c3d5fc1361ae8e13d05e6849b358dc3f2c279"}, + {file = "scipy-1.7.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca36e7d9430f7481fc7d11e015ae16fbd5575615a8e9060538104778be84addf"}, + {file = "scipy-1.7.3-cp37-cp37m-win32.whl", hash = "sha256:e2c036492e673aad1b7b0d0ccdc0cb30a968353d2c4bf92ac8e73509e1bf212c"}, + {file = "scipy-1.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:866ada14a95b083dd727a845a764cf95dd13ba3dc69a16b99038001b05439709"}, + {file = "scipy-1.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:65bd52bf55f9a1071398557394203d881384d27b9c2cad7df9a027170aeaef93"}, + {file = "scipy-1.7.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:f99d206db1f1ae735a8192ab93bd6028f3a42f6fa08467d37a14eb96c9dd34a3"}, + {file = "scipy-1.7.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5f2cfc359379c56b3a41b17ebd024109b2049f878badc1e454f31418c3a18436"}, + {file = "scipy-1.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb7ae2c4dbdb3c9247e07acc532f91077ae6dbc40ad5bd5dca0bb5a176ee9bda"}, + {file = "scipy-1.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c2d250074cfa76715d58830579c64dff7354484b284c2b8b87e5a38321672c"}, + {file = "scipy-1.7.3-cp38-cp38-win32.whl", hash = "sha256:87069cf875f0262a6e3187ab0f419f5b4280d3dcf4811ef9613c605f6e4dca95"}, + {file = "scipy-1.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:7edd9a311299a61e9919ea4192dd477395b50c014cdc1a1ac572d7c27e2207fa"}, + {file = "scipy-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eef93a446114ac0193a7b714ce67659db80caf940f3232bad63f4c7a81bc18df"}, + {file = "scipy-1.7.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:eb326658f9b73c07081300daba90a8746543b5ea177184daed26528273157294"}, + {file = "scipy-1.7.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:93378f3d14fff07572392ce6a6a2ceb3a1f237733bd6dcb9eb6a2b29b0d19085"}, + {file = "scipy-1.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edad1cf5b2ce1912c4d8ddad20e11d333165552aba262c882e28c78bbc09dbf6"}, + {file = "scipy-1.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d1cc2c19afe3b5a546ede7e6a44ce1ff52e443d12b231823268019f608b9b12"}, + {file = "scipy-1.7.3-cp39-cp39-win32.whl", hash = "sha256:2c56b820d304dffcadbbb6cbfbc2e2c79ee46ea291db17e288e73cd3c64fefa9"}, + {file = "scipy-1.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:3f78181a153fa21c018d346f595edd648344751d7f03ab94b398be2ad083ed3e"}, + {file = "scipy-1.7.3.tar.gz", hash = "sha256:ab5875facfdef77e0a47d5fd39ea178b58e60e454a4c85aa1e52fcb80db7babf"}, +] +seaborn = [ + {file = "seaborn-0.11.2-py3-none-any.whl", hash = "sha256:85a6baa9b55f81a0623abddc4a26b334653ff4c6b18c418361de19dbba0ef283"}, + {file = "seaborn-0.11.2.tar.gz", hash = "sha256:cf45e9286d40826864be0e3c066f98536982baf701a7caa386511792d61ff4f6"}, +] +send2trash = [ + {file = "Send2Trash-1.8.0-py3-none-any.whl", hash = "sha256:f20eaadfdb517eaca5ce077640cb261c7d2698385a6a0f072a4a5447fd49fa08"}, + {file = "Send2Trash-1.8.0.tar.gz", hash = "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d"}, +] +setuptools-scm = [ + {file = "setuptools_scm-6.3.2-py3-none-any.whl", hash = "sha256:4c64444b1d49c4063ae60bfe1680f611c8b13833d556fd1d6050c0023162a119"}, + {file = "setuptools_scm-6.3.2.tar.gz", hash = "sha256:a49aa8081eeb3514eb9728fa5040f2eaa962d6c6f4ec9c32f6c1fba88f88a0f2"}, +] +simpy = [ + {file = "simpy-4.0.1-py2.py3-none-any.whl", hash = "sha256:6c90c105628a5b4c9c3ee822b45b9980c60d3126b99295b616bb72cd69b6a35e"}, + {file = "simpy-4.0.1.tar.gz", hash = "sha256:b36542e2faab612f861c5ef4da17220ac1553f5892b3583c67281dbe4faad404"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +terminado = [ + {file = "terminado-0.12.1-py3-none-any.whl", hash = "sha256:09fdde344324a1c9c6e610ee4ca165c4bb7f5bbf982fceeeb38998a988ef8452"}, + {file = "terminado-0.12.1.tar.gz", hash = "sha256:b20fd93cc57c1678c799799d117874367cc07a3d2d55be95205b1a88fa08393f"}, +] +testpath = [ + {file = "testpath-0.5.0-py3-none-any.whl", hash = "sha256:8044f9a0bab6567fc644a3593164e872543bb44225b0e24846e2c89237937589"}, + {file = "testpath-0.5.0.tar.gz", hash = "sha256:1acf7a0bcd3004ae8357409fc33751e16d37ccc650921da1094a86581ad1e417"}, +] +tomli = [ + {file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"}, + {file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, +] +tornado = [ + {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, + {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, + {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, + {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, + {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, + {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, + {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, + {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, + {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, + {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, + {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, + {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, + {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, + {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, + {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, + {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, + {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, + {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, +] +traitlets = [ + {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"}, + {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] +widgetsnbextension = [ + {file = "widgetsnbextension-3.5.2-py2.py3-none-any.whl", hash = "sha256:763a9fdc836d141fa080005a886d63f66f73d56dba1fb5961afc239c77708569"}, + {file = "widgetsnbextension-3.5.2.tar.gz", hash = "sha256:e0731a60ba540cd19bbbefe771a9076dcd2dde90713a8f87f27f53f2d1db7727"}, +] +zipp = [ + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8fa71f9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "blockchain-engineering" +version = "1.0.0" +description = "A collection of notebooks that helps to design and simulate own blockchain-like system from scratch." +authors = ["grimadas "] +license = "MIT" +readme = "README.md" +homepage = "https://github.com/grimadas/BlockchainEngineering" +repository = "https://github.com/grimadas/BlockchainEngineering" +keywords = ["simulation","blockchain","notebook","distributed-systems"] + +[tool.poetry.dependencies] +python = ">=3.8,<3.11" +p2psimpy = "^0.1.0" +pandas = "^1.3.5" +jupyter = "^1.0.0" +ipykernel = "^6.6.0" +matplotlib = "^3.5.1" +seaborn = "^0.11.2" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api"