├── SG ├── __init__.py └── BFSG.py ├── img └── BFSG.gif ├── .editorconfig ├── tests ├── __init__.py └── test_brute_froce_string_generator.py ├── Pipfile ├── setup.py ├── .github └── dependabot.yml ├── LICENSE ├── .gitignore ├── README.md └── Pipfile.lock /SG/__init__.py: -------------------------------------------------------------------------------- 1 | from SG.BFSG import BruteForceStringGenerator 2 | -------------------------------------------------------------------------------- /img/BFSG.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yezz123-Archive/BFSG/HEAD/img/BFSG.gif -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = false -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | def run_test(): 5 | test_loader = unittest.TestLoader() 6 | test_suite = test_loader.discover('tests/', pattern='test_*.py') 7 | return test_suite 8 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | mypy = "*" 8 | 9 | [dev-packages] 10 | pytest = "*" 11 | mypy = "*" 12 | 13 | [requires] 14 | python_version = "3.8.5" 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="BFSG", 8 | version="1.0", 9 | author="Yezz123", 10 | author_email="yasserth19@protonmail.com", 11 | description="String bruteforce generator", 12 | long_description=long_description, 13 | packages=setuptools.find_packages(), 14 | install_requires=[ 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Collector 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | 107 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | 7 | 8 | 9 | 10 | 11 | Star Badge 12 |

13 | 14 | ## BFSG 🌙 15 | 16 | BFSG is a web BruteForce String generator written in [Python](https://www.python.org/) 17 | 18 | ## How i can start using BFSG 😎 19 | 20 | - First check that you have a python version 3.8.5 or update it from [Python Update](https://www.python.org/downloads/) 21 | 22 | ### Installation 🤘 23 | 24 | ``` 25 | git clone https://github.com/yezz123/BFSG.git 26 | 27 | Cd BFSG 28 | ``` 29 | ### Usage 😁 30 | 31 | - Development prerequisites 32 | 33 | ``` 34 | pipenv install 35 | ``` 36 | 37 | - Production prerequisites 38 | 39 | ``` 40 | pipenv install --dev 41 | ``` 42 | 43 | - To activate project's virtualenv run: 44 | ``` 45 | pipenv shell 46 | ``` 47 | ### Running the tests 🥱 48 | 49 | ``` 50 | cd BFSG 51 | 52 | mypy BFSG.py 53 | 54 | pytest 55 | ``` 56 | ## Contributing ⭐ 57 | 58 | Contributions are welcome! ❤ Please share any features, and add unit tests! Use the pull request and issue systems to contribute. 59 | 60 | ## Credits & Thanks 🏆 61 | 62 |

63 | 64 | Medium 65 | 66 | Twitter 67 | 68 | Discord 69 |

70 | -------------------------------------------------------------------------------- /SG/BFSG.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import string 3 | from enum import IntEnum 4 | 5 | 6 | class Direction(IntEnum): 7 | LEFT = 0 8 | RIGHT = -1 9 | 10 | 11 | class BruteForceStringGenerator: 12 | 13 | def __init__(self, sequence: str = '', chars: str = string.ascii_lowercase, direction: Direction = Direction.RIGHT, 14 | min_length: int = 1, max_length: int = 0) -> None: 15 | 16 | self.sequence = sequence 17 | self._sequence_list = list(sequence) 18 | self.chars = chars 19 | self.dir = direction 20 | self.min_length = max(0, min_length) 21 | self.max_length = max(0, max_length) 22 | self.chars_num = len(self.chars) 23 | 24 | def __iter__(self) -> BruteForceStringGenerator: 25 | return self 26 | 27 | def __next__(self) -> str: 28 | self.next_string() 29 | return self.sequence 30 | 31 | def __len__(self) -> int: 32 | return len(self._sequence_list) 33 | 34 | def __repr__(self): 35 | return f"{self.sequence}" 36 | 37 | @property 38 | def sequence(self) -> str: 39 | return "".join(self._sequence_list) 40 | 41 | @sequence.setter 42 | def sequence(self, sequence: str) -> None: 43 | self._sequence_list = list(sequence) 44 | 45 | @property 46 | def dir(self): 47 | return self._dir 48 | 49 | @dir.setter 50 | def dir(self, direction): 51 | if type(direction) is not Direction: 52 | raise ValueError("Direction should be Direction Type") 53 | self._dir = direction 54 | 55 | @property 56 | def min_length(self): 57 | return self._min_length 58 | 59 | @min_length.setter 60 | def min_length(self, min_length): 61 | if type(min_length) is not int or min_length <= 0: 62 | raise ValueError("min_length should be integer greater than 0") 63 | self._min_length = min_length 64 | 65 | @property 66 | def max_length(self): 67 | return self._max_length 68 | 69 | @max_length.setter 70 | def max_length(self, max_length): 71 | if type(max_length) is not int or max_length < 0: 72 | raise ValueError("max_length should be integer") 73 | self._max_length = max_length 74 | 75 | def next_string(self) -> None: 76 | self._sequence_list = self._next(self._sequence_list) 77 | 78 | def check_length(self, length: int): 79 | if self.max_length and length > self.max_length: 80 | raise StopIteration 81 | 82 | def _next(self, current: list) -> list: 83 | if len(current) <= 0: 84 | if not self._sequence_list: 85 | return list(self.chars[0] * self.min_length) 86 | else: 87 | self.check_length(len(self) + 1) 88 | return list(self.chars[0]) 89 | else: 90 | self.check_length(len(self)) 91 | current[self.dir] = self.chars[((self.chars.index(current[self.dir]) + 1) % self.chars_num)] 92 | if self.chars.index(current[self.dir]) == 0: 93 | if self.dir == Direction.LEFT: 94 | return list(current[0]) + self._next(current[1:]) 95 | else: 96 | return self._next(current[:-1]) + list(current[-1]) 97 | return current 98 | -------------------------------------------------------------------------------- /tests/test_brute_froce_string_generator.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | from SG.BFSG import BruteForceStringGenerator, Direction 4 | 5 | import unittest 6 | import pytest 7 | 8 | 9 | class MyTest(unittest.TestCase): 10 | def test_brute_force_empty(self): 11 | default_init = BruteForceStringGenerator() 12 | assert (next(default_init) == 'a') 13 | 14 | def test_brute_force_direction(self): 15 | direction_test = BruteForceStringGenerator(sequence='aa', direction=Direction.LEFT) 16 | assert (next(direction_test) == 'ba') 17 | direction_test = BruteForceStringGenerator(sequence='aa', direction=Direction.RIGHT) 18 | assert (next(direction_test) == 'ab') 19 | 20 | def test_brute_force_edge(self): 21 | direction_test = BruteForceStringGenerator(sequence='za', direction=Direction.LEFT) 22 | assert (next(direction_test) == 'ab') 23 | direction_test = BruteForceStringGenerator(sequence='az', direction=Direction.RIGHT) 24 | assert (next(direction_test) == 'ba') 25 | 26 | def test_brute_force_long_string(self): 27 | long_string = BruteForceStringGenerator(sequence='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') 28 | assert (next(long_string) == 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') 29 | 30 | def test_brute_force_for_loop(self): 31 | loop_string = BruteForceStringGenerator(min_length=3) 32 | for _ in range(1, len(string.ascii_lowercase)): 33 | next(loop_string) 34 | assert (next(loop_string) == 'aaz') 35 | 36 | for _ in range(1, len(string.ascii_lowercase)): 37 | next(loop_string) 38 | assert (next(loop_string) == 'abz') 39 | 40 | for _ in range(1, len(string.ascii_lowercase * len(string.ascii_lowercase))): 41 | next(loop_string) 42 | assert (next(loop_string) == 'bbz') 43 | 44 | def test_brute_force_custom_chars(self): 45 | custom_string = BruteForceStringGenerator(chars='!@#', min_length=3) 46 | assert (next(custom_string) == '!!!') 47 | assert (next(custom_string) == '!!@') 48 | assert (next(custom_string) == '!!#') 49 | 50 | def test_brute_force_brute_left(self): 51 | brute_string = BruteForceStringGenerator(direction=Direction.LEFT) 52 | for _ in range(1, 2000000): 53 | next(brute_string) 54 | assert (next(brute_string) == 'botid') 55 | 56 | def test_brute_force_brute_right(self): 57 | brute_string = BruteForceStringGenerator() 58 | for _ in range(1, 2000000): 59 | next(brute_string) 60 | assert (next(brute_string) == 'ditob') 61 | 62 | def test_brute_force_max_length(self): 63 | with pytest.raises(StopIteration): 64 | brute_string = BruteForceStringGenerator('z', max_length=1) 65 | next(brute_string) 66 | 67 | def test_brute_force_generator(self): 68 | brute_string = BruteForceStringGenerator(max_length=3) 69 | for text in brute_string: 70 | pass 71 | assert (text == 'zzz') 72 | 73 | def test_not_direction_type(self): 74 | for i in range(-1, 2): 75 | with pytest.raises(ValueError): 76 | BruteForceStringGenerator('a', max_length=1, direction=i) 77 | 78 | def test_brute_force_with_space(self): 79 | custom_string = BruteForceStringGenerator(chars='a ', min_length=3) 80 | assert (next(custom_string) == 'aaa') 81 | assert (next(custom_string) == 'aa ') 82 | assert (next(custom_string) == 'a a') 83 | 84 | def test_brute_force_with_polish_letters(self): 85 | custom_string = BruteForceStringGenerator(chars='ąćęłńóśźż', min_length=3) 86 | for _ in range(1, 2003): 87 | next(custom_string) 88 | assert (next(custom_string) == 'ćśśń') 89 | 90 | def test_brute_force_with_polish_letters_backward(self): 91 | custom_string = BruteForceStringGenerator(chars='ąćęłńóśźż', min_length=3, direction=Direction.LEFT) 92 | for _ in range(1, 2003): 93 | next(custom_string) 94 | assert (next(custom_string) == 'ńśść') 95 | 96 | def test_brute_force_with_german_letters(self): 97 | custom_string = BruteForceStringGenerator(chars='äöüß', min_length=3) 98 | for _ in range(1, 2003): 99 | next(custom_string) 100 | assert (next(custom_string) == 'äüüöäü') 101 | 102 | def test_brute_force_with_german_letters_backward(self): 103 | custom_string = BruteForceStringGenerator(chars='äöüß', min_length=3, direction=Direction.LEFT) 104 | for _ in range(1, 2003): 105 | next(custom_string) 106 | assert (next(custom_string) == 'üäöüüä') 107 | 108 | def test_brute_force_with_czech_letters(self): 109 | custom_string = BruteForceStringGenerator(chars='ýžáčďéěíňóřšťúů', max_length=4) 110 | for _ in custom_string: 111 | pass 112 | assert (custom_string.sequence == 'ůůůý') 113 | 114 | def test_brute_force_with_czech_letters_backward(self): 115 | custom_string = BruteForceStringGenerator(chars='ýžáčďéěíňóřšťúů', max_length=4, direction=Direction.LEFT) 116 | for _ in custom_string: 117 | pass 118 | assert (custom_string.sequence == 'ýůůů') 119 | 120 | def test_brute_force_string_max_length(self): 121 | for i in range(1, 10): 122 | custom_string = BruteForceStringGenerator(chars='ab', max_length=i) 123 | for _ in custom_string: 124 | pass 125 | assert (len(custom_string) == i) 126 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "737970ea335caaae8657acf6f544e3bd6164bc8688dadedd815926a975b4c91c" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.8.5" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.python.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "mypy": { 20 | "hashes": [ 21 | "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9", 22 | "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a", 23 | "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9", 24 | "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e", 25 | "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2", 26 | "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212", 27 | "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b", 28 | "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885", 29 | "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150", 30 | "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703", 31 | "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072", 32 | "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457", 33 | "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e", 34 | "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0", 35 | "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb", 36 | "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97", 37 | "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8", 38 | "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811", 39 | "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6", 40 | "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de", 41 | "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504", 42 | "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921", 43 | "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d" 44 | ], 45 | "index": "pypi", 46 | "version": "==0.910" 47 | }, 48 | "mypy-extensions": { 49 | "hashes": [ 50 | "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", 51 | "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" 52 | ], 53 | "version": "==0.4.3" 54 | }, 55 | "toml": { 56 | "hashes": [ 57 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", 58 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" 59 | ], 60 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 61 | "version": "==0.10.2" 62 | }, 63 | "typing-extensions": { 64 | "hashes": [ 65 | "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", 66 | "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", 67 | "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34" 68 | ], 69 | "version": "==3.10.0.2" 70 | } 71 | }, 72 | "develop": { 73 | "attrs": { 74 | "hashes": [ 75 | "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", 76 | "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" 77 | ], 78 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 79 | "version": "==21.2.0" 80 | }, 81 | "iniconfig": { 82 | "hashes": [ 83 | "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", 84 | "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" 85 | ], 86 | "version": "==1.1.1" 87 | }, 88 | "mypy": { 89 | "hashes": [ 90 | "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9", 91 | "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a", 92 | "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9", 93 | "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e", 94 | "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2", 95 | "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212", 96 | "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b", 97 | "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885", 98 | "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150", 99 | "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703", 100 | "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072", 101 | "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457", 102 | "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e", 103 | "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0", 104 | "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb", 105 | "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97", 106 | "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8", 107 | "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811", 108 | "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6", 109 | "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de", 110 | "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504", 111 | "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921", 112 | "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d" 113 | ], 114 | "index": "pypi", 115 | "version": "==0.910" 116 | }, 117 | "mypy-extensions": { 118 | "hashes": [ 119 | "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", 120 | "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" 121 | ], 122 | "version": "==0.4.3" 123 | }, 124 | "packaging": { 125 | "hashes": [ 126 | "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7", 127 | "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14" 128 | ], 129 | "markers": "python_version >= '3.6'", 130 | "version": "==21.0" 131 | }, 132 | "pluggy": { 133 | "hashes": [ 134 | "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", 135 | "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" 136 | ], 137 | "markers": "python_version >= '3.6'", 138 | "version": "==1.0.0" 139 | }, 140 | "py": { 141 | "hashes": [ 142 | "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", 143 | "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" 144 | ], 145 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 146 | "version": "==1.10.0" 147 | }, 148 | "pyparsing": { 149 | "hashes": [ 150 | "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", 151 | "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" 152 | ], 153 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 154 | "version": "==2.4.7" 155 | }, 156 | "pytest": { 157 | "hashes": [ 158 | "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89", 159 | "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134" 160 | ], 161 | "index": "pypi", 162 | "version": "==6.2.5" 163 | }, 164 | "toml": { 165 | "hashes": [ 166 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", 167 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" 168 | ], 169 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 170 | "version": "==0.10.2" 171 | }, 172 | "typing-extensions": { 173 | "hashes": [ 174 | "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", 175 | "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", 176 | "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34" 177 | ], 178 | "version": "==3.10.0.2" 179 | } 180 | } 181 | } 182 | --------------------------------------------------------------------------------