-
Notifications
You must be signed in to change notification settings - Fork 27
[WIP] Tikhon Puntus #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
PythonProjectDeveloper
wants to merge
20
commits into
PythonAndGoCourses2:master
Choose a base branch
from
PythonProjectDeveloper:dev
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
43fc5e7
feat: implement code
PythonProjectDeveloper 729d119
fix: change setup.py
PythonProjectDeveloper 721d116
fix: change setup.py
PythonProjectDeveloper 775cccc
fix: change setup.py
PythonProjectDeveloper c1d5a20
fix: change setup.py
PythonProjectDeveloper 89b8d2a
fix: change setup.py
PythonProjectDeveloper 1087473
refactor: make regular expressions in separate file
PythonProjectDeveloper 3c8094b
fix: return to its former state
PythonProjectDeveloper a33d56b
refactor: make regular expressions in separate file
PythonProjectDeveloper 2c0dbec
refactor: carry out operators in the dictionary
PythonProjectDeveloper 308bc29
refactor: change file name math_regexp to regexp
PythonProjectDeveloper bc75852
refactor: split code into multiple files
PythonProjectDeveloper cdb9a8f
refactor: change style of code to pycodestyle
PythonProjectDeveloper 3d20b3a
docs: add docs for files
PythonProjectDeveloper 0d4c494
refactor: rename variables
PythonProjectDeveloper d7abb7f
feat: update setup.py
PythonProjectDeveloper b64f2ae
refactor: add blank lines and __init__ file
PythonProjectDeveloper 21fccf7
refactor: change style of file import
PythonProjectDeveloper f3ed670
refactor: remove unnecessary imports
PythonProjectDeveloper 7412ff4
refactor: decompose files into folders
PythonProjectDeveloper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| from .checker import ( | ||
| check_spaces, | ||
| check_brackets, | ||
| check_constant, | ||
| check_function, | ||
| check_expression, | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| # -*- coding: utf-8 -*- | ||
| """ | ||
| The module is designed to test the mathematical expression for correctness. | ||
|
|
||
| Example: | ||
| check_spaces(' 1+ 3 / 2') | ||
| >>> '1+3/2' | ||
|
|
||
| check_brackets('2*(3+5)') | ||
| >>> '2*(3+5)' | ||
|
|
||
| lib = { | ||
| 'e': 2.718281828459045, | ||
| 'sum': sum | ||
| } | ||
| check_function('sum(100, 50)', lib) | ||
| >>> 'sum(100, 50)' | ||
| """ | ||
|
|
||
| import re | ||
| from ..library import Library | ||
| from ..operators import ( | ||
| LEFT_BRACKET, | ||
| RIGHT_BRACKET, | ||
| ) | ||
| from ..regexp import ( | ||
| REGEXP_INCORECT_EXPRETION, | ||
| REGEXP_CONSTANT, | ||
| REGEXP_DIGIT, | ||
| REGEXP_FUNCTION, | ||
| ) | ||
|
|
||
|
|
||
| def check_spaces(expr: str) -> str: | ||
| """ | ||
| Checks if an expression has the wrong elements. | ||
|
|
||
| Args: | ||
| expr (str): String mathematical expression. | ||
|
|
||
| Returns: | ||
| str: cleared expression from spaces. | ||
|
|
||
| Raises: | ||
| ValueError: If `expr` is not correct`. | ||
| """ | ||
| matches = re.findall(REGEXP_INCORECT_EXPRETION, expr) | ||
| if matches: | ||
| raise ValueError('expression is not correct') | ||
|
|
||
| return expr.replace(' ', '') | ||
|
|
||
|
|
||
| def check_brackets(expr: str): | ||
| """ | ||
| Checks if all brackets have a pair. | ||
|
|
||
| Args: | ||
| expr (str): String mathematical expression. | ||
|
|
||
| Raises: | ||
| ValueError: If `expr` is not correct`. | ||
| """ | ||
| stack = [] | ||
| for symbol in expr: | ||
| if symbol == LEFT_BRACKET: | ||
| stack.append(symbol) | ||
| elif symbol == RIGHT_BRACKET and (not stack or stack.pop() != LEFT_BRACKET): | ||
| raise ValueError('brackets are not balanced') | ||
|
|
||
| if stack: | ||
| raise ValueError('brackets are not balanced') | ||
|
|
||
|
|
||
| def check_constant(expr: str, library: Library): | ||
| """ | ||
| Checks if all constants in the expression are available. | ||
|
|
||
| Args: | ||
| expr (str): String mathematical expression. | ||
| library (Library): dictionary of functions and constant. | ||
|
|
||
| Raises: | ||
| ValueError: If `expr` is not correct`. | ||
| """ | ||
| matches = re.finditer(REGEXP_CONSTANT, expr) | ||
| for match in matches: | ||
| name = match.group('name') | ||
|
|
||
| if name[-1] == LEFT_BRACKET or re.match(REGEXP_DIGIT, name): | ||
| continue | ||
|
|
||
| if name[0].isdigit(): | ||
| raise ValueError(f'constant {name} can not start with digit') | ||
|
|
||
| if name not in library or callable(library[name]): | ||
| raise ValueError(f'there is no such constant {name}') | ||
|
|
||
|
|
||
| def check_function(expr: str, library: Library): | ||
| """ | ||
| Checks if all functions in the expression are available. | ||
|
|
||
| Args: | ||
| expr (str): String mathematical expression. | ||
| library (Library): dictionary of functions and constant. | ||
|
|
||
| Raises: | ||
| ValueError: If `expr` is not correct`. | ||
| """ | ||
| matches = re.finditer(REGEXP_FUNCTION, expr) | ||
| for match in matches: | ||
| name = match.group('name') | ||
| pattern = match.group('pattern') | ||
|
|
||
| if name[0].isdigit(): | ||
| raise ValueError(f'function {pattern} can not start with digit') | ||
|
|
||
| if name not in library or not callable(library[name]): | ||
| raise ValueError(f'there is no such function {pattern}') | ||
|
|
||
|
|
||
| def check_expression(expr: str, library: Library) -> str: | ||
| """ | ||
| Checks the expression for correctness. | ||
|
|
||
| Args: | ||
| expr (str): String mathematical expression. | ||
| library (Library): dictionary of functions and constant. | ||
|
|
||
| Returns: | ||
| str: cleared expression. | ||
|
|
||
| Raises: | ||
| ValueError: If `expr` is not correct`. | ||
| """ | ||
| expr = check_spaces(expr) | ||
| check_brackets(expr) | ||
| check_constant(expr, library) | ||
| check_function(expr, library) | ||
|
|
||
| return expr |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import unittest | ||
| from ..library import Library | ||
| from .checker import ( | ||
| check_brackets, | ||
| check_constant, | ||
| check_expression, | ||
| check_function, | ||
| check_spaces, | ||
| ) | ||
|
|
||
|
|
||
| class TestCheckFunction(unittest.TestCase): | ||
| @classmethod | ||
| def setUpClass(cls): | ||
| cls.lib = Library('math') | ||
|
|
||
| def test_check_spaces(self): | ||
| with self.subTest("Throws error if spaces is not correct"): | ||
| self.assertRaises(ValueError, lambda: check_spaces('')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('------')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('-')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('+')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('1-')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('1 + 1 2 3 4 5 6 ')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('* *')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('/ /')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('/ *')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('+ *')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('1 2')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('= =')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('! =')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('<-+!')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('==7')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('1 + 2(3 * 4))')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('1 = = 2')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('1<>2')) | ||
| self.assertRaises(ValueError, lambda: check_spaces('1><2')) | ||
|
|
||
| with self.subTest("removes spaces and returns new expretion"): | ||
| self.assertEqual(check_spaces('1 + 2'), '1+2') | ||
| self.assertEqual(check_spaces('1-2'), '1-2') | ||
| self.assertEqual(check_spaces('1 * - 2'), '1*-2') | ||
| self.assertEqual(check_spaces('1 == 2'), '1==2') | ||
| self.assertEqual(check_spaces('1 <= 2'), '1<=2') | ||
| self.assertEqual(check_spaces('1 - sin (1, 2, 3) + - 2'), '1-sin(1,2,3)+-2') | ||
| self.assertEqual(check_spaces('sin(pi/2)'), 'sin(pi/2)') | ||
| self.assertTrue(check_spaces('sin(e^log(e^e^sin(23.0),45.0)+cos(3.0+log10(e^-e)))')) | ||
| self.assertTrue(check_spaces('time()-e-(1+1)/60+1-1*1//10000%1000^2==1==1<=3>=5<1>1')) | ||
|
|
||
| val = ('sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(log10(43.0))))' | ||
| '+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0)') | ||
| self.assertTrue(check_spaces(val)) | ||
|
|
||
| def test_check_brackets(self): | ||
| with self.subTest("Throws error if brackets are not unpaired"): | ||
| self.assertRaises(ValueError, lambda: check_brackets('(')) | ||
| self.assertRaises(ValueError, lambda: check_brackets(')')) | ||
| self.assertRaises(ValueError, lambda: check_brackets('())(')) | ||
| self.assertRaises(ValueError, lambda: check_brackets('(()))')) | ||
| self.assertRaises(ValueError, lambda: check_brackets(')()(')) | ||
|
|
||
| with self.subTest("returns nothing if expretion is good"): | ||
| self.assertIsNone(check_brackets('')) | ||
| self.assertIsNone(check_brackets('()')) | ||
| self.assertIsNone(check_brackets('((()))()')) | ||
| self.assertIsNone(check_brackets('()()()()')) | ||
| self.assertIsNone(check_brackets('(()(()())())')) | ||
|
|
||
| def test_check_constant(self): | ||
| with self.subTest("Throws error if environment does not have constant"): | ||
| self.assertRaises(ValueError, lambda: check_constant('constant', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_constant('constant + 5', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_constant('sin(1) + constant + 7', self.lib)) | ||
|
|
||
| with self.subTest("Throws error if constant name starts with digit"): | ||
| self.assertRaises(ValueError, lambda: check_constant('10constant', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_constant('10constant + 5', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_constant('sin(1) + 10constant + 7', self.lib)) | ||
|
|
||
| with self.subTest("returns nothing if expretion is good"): | ||
| self.assertIsNone(check_constant('', self.lib)) | ||
| self.assertIsNone(check_constant('e', self.lib)) | ||
| self.assertIsNone(check_constant('sin(21)', self.lib)) | ||
| self.assertIsNone(check_constant('sin(21) + e', self.lib)) | ||
| self.assertIsNone(check_constant('2.4178516392292583e+24 + 5', self.lib)) | ||
|
|
||
| def test_check_function(self): | ||
| with self.subTest("Throws error if environment does not have function"): | ||
| self.assertRaises(ValueError, lambda: check_function('multiply()', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_function('multiply(5,7)', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_function('multiply() + 7', self.lib)) | ||
|
|
||
| with self.subTest("Throws error if function name starts with digit"): | ||
| self.assertRaises(ValueError, lambda: check_function('10log()', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_function('10log(1)', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_function('10log(5,7)', self.lib)) | ||
| self.assertRaises(ValueError, lambda: check_function('10log() + 7', self.lib)) | ||
|
|
||
| with self.subTest("returns nothing if expretion is good"): | ||
| self.assertIsNone(check_function('', self.lib)) | ||
| self.assertIsNone(check_function('e', self.lib)) | ||
| self.assertIsNone(check_function('sin(21)', self.lib)) | ||
| self.assertIsNone(check_function('sin(21) + e', self.lib)) | ||
| self.assertIsNone(check_function('2.4178516392292583e+24 + 5', self.lib)) | ||
|
|
||
| def test_check_expression(self): | ||
| with self.subTest("returns expression without spaces"): | ||
| self.assertEqual(check_expression('1 + 2', self.lib), '1+2') | ||
| self.assertEqual(check_expression('1-2', self.lib), '1-2') | ||
| self.assertEqual(check_expression('1 * - 2', self.lib), '1*-2') | ||
| self.assertEqual(check_expression('1 - sin (1, 2, 3) + - 2', self.lib), '1-sin(1,2,3)+-2') | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from .converter import convert_answer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # -*- coding: utf-8 -*- | ||
| """ | ||
| The module is designed to convert the expression to the desired type. | ||
|
|
||
| Example: | ||
| convert_answer('-1', False) | ||
| >>> '-1' | ||
|
|
||
| convert_answer('-1', False) | ||
| >>> '0' | ||
|
|
||
| convert_answer('-1', True) | ||
| >>> 'True' | ||
|
|
||
| convert_answer('0', True) | ||
| >>> 'False' | ||
| """ | ||
|
|
||
| from ..regexp import has_non_zero_fraction_part | ||
|
|
||
|
|
||
| def convert_answer(expr: str, has_compare: bool) -> str: | ||
| """ | ||
| Converts the resulting string to the desired type. | ||
|
|
||
| Args: | ||
| expr (str): String representation of a number. | ||
| has_compare (bool): whether the expression contains boolean logic | ||
| """ | ||
| num = float(expr) | ||
| match = has_non_zero_fraction_part(expr) | ||
| num = num if match else int(num) | ||
|
|
||
| result = bool(num) if has_compare else num | ||
|
|
||
| return str(result) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import unittest | ||
| from .converter import convert_answer | ||
|
|
||
|
|
||
| class TestConverterFunction(unittest.TestCase): | ||
| def test_convert_answer(self): | ||
| with self.subTest("returns correct answer"): | ||
| self.assertEqual(convert_answer('-1', False), '-1') | ||
| self.assertEqual(convert_answer('0', False), '0') | ||
| self.assertEqual(convert_answer('-1', True), 'True') | ||
| self.assertEqual(convert_answer('0', True), 'False') | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from .library import Library |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # -*- coding: utf-8 -*- | ||
| """ | ||
| The module is designed for dynamic creation of module libraries. | ||
|
|
||
| Example: | ||
| lib = Library('math', 'os') | ||
| lib.update('time', 'os') | ||
|
|
||
| lib['e'] | ||
| >>> 2.718281828459045 | ||
|
|
||
| lib['sum'](5, 10) | ||
| >>> 15 | ||
| """ | ||
|
|
||
|
|
||
| class Library(dict): | ||
| """ | ||
| Class is designed to work with modules. | ||
| It is a dictionary of functions and constants. | ||
| """ | ||
| def __init__(self, *modules: list): | ||
| super().__init__() | ||
|
|
||
| self['abs'] = abs | ||
| self['round'] = round | ||
|
|
||
| self.update(*modules) | ||
|
|
||
| def update(self, *modules: list): | ||
| """Adds functions and veriables from got module names to dictionary.""" | ||
| for module in modules: | ||
| super().update(__import__(module).__dict__) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import unittest | ||
| from .library import Library | ||
|
|
||
|
|
||
| class TestLibraryClass(unittest.TestCase): | ||
| def test__init__(self): | ||
| with self.subTest("contains around and abs functon by default"): | ||
| lib = Library() | ||
|
|
||
| self.assertTrue('round' in lib) | ||
| self.assertTrue('abs' in lib) | ||
|
|
||
| with self.subTest("can get module names and adds their variables to your own dictionary"): | ||
| lib = Library('math', 'os', 'time') | ||
|
|
||
| self.assertTrue('path' in lib) | ||
| self.assertTrue('clock' in lib) | ||
| self.assertTrue('pi' in lib) | ||
|
|
||
| def test_update(self): | ||
| with self.subTest("get module names and adds their variables to your own dictionary"): | ||
| lib = Library() | ||
| self.assertFalse('path' in lib) | ||
|
|
||
| lib.update('os') | ||
| self.assertTrue('path' in lib) | ||
|
|
||
| lib.update('sys', 'time') | ||
| self.assertTrue('stdin' in lib) | ||
| self.assertTrue('clock' in lib) | ||
|
|
||
| with self.subTest("raises error if veriable is not found"): | ||
| self.assertRaises(ModuleNotFoundError, lambda: lib.update('bad_module')) | ||
| self.assertRaises(ModuleNotFoundError, lambda: lib.update('new_math')) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Использование класса с родителем
dictв этом случае выглядит слишком сложным способом решния нужной задачи. Можно обойтись обычной функцией, которая будет возвращаться нужный словарь.