diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fa016a12c85588f0528e80d0e3117ddb947a9b45 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +venv +.mypy_cache +.DS_Store +__pycache__/ diff --git a/README.md b/README.md index dc93e0970223eb84b75366eb0317d4f7457e998a..46f0afa221b5bc48762dbd045978dcd525ef6d50 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,26 @@ # Python Knowledge Base +This in project with examples from [Python knowledge base](https://wiki.itrexgroup.com/display/PKB) +Here you can run, change or just play with any example for better understanding, +hope these examples will help you to improve your level. +If you have any suggestions please contact dima.kliuchnik@itrexgroup.com or yaroslav.sidoruk@itrexgroup.com ## Getting started -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +Setup [black in pycharm](https://black.readthedocs.io/en/stable/integrations/editors.html) +## Installation ``` -cd existing_repo -git remote add origin https://gitlab.itrexgroup.com/dima.kliuchnik/python-knowledge-base.git -git branch -M main -git push -uf origin main +cd +git clone https://gitlab.itrexgroup.com/dima.kliuchnik/python-knowledge-base.git +virtualenv venv -p python3 +source venv/bin/activate +pip3 install -r requirements.txt +nox ``` -## Integrate with your tools - -- [ ] [Set up project integrations](https://gitlab.itrexgroup.com/dima.kliuchnik/python-knowledge-base/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - ## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +Each module file has its own examples, so you can run them in it. +To lint and format code use command `nox` \ No newline at end of file diff --git a/closures/multiplier.py b/closures/multiplier.py index ed3aedd8aac10960097e1c0ffbc647498e81e0f5..0e372f9e4baf68cde01a8db5b11afb0c42e0b683 100644 --- a/closures/multiplier.py +++ b/closures/multiplier.py @@ -1,7 +1,10 @@ -def make_multiplier_of(n): +from collections import Callable + + +def make_multiplier_of(n: int) -> Callable: txt = f"muliptication on {n} function was called" - def multiplier(x): + def multiplier(x: int) -> int: print(txt) return x * n @@ -18,7 +21,7 @@ if __name__ == "__main__": # cell object print(times3.__closure__) # find what values are stored inside cells - print(times3.__closure__[0].cell_contents) - print(times3.__closure__[1].cell_contents) + print(times3.__closure__[0].cell_contents) # type: ignore + print(times3.__closure__[1].cell_contents) # type: ignore # get all variables names stored in closure print(times3.__code__.co_freevars) diff --git a/closures/point.py b/closures/point.py index 93478ff77482d81b9200c74ee490cba3f7ab8f79..1ab21613eb3104b13df7e57f9b34ba27c5fb2f67 100644 --- a/closures/point.py +++ b/closures/point.py @@ -1,35 +1,38 @@ -def make_point(x, y): - def point(): +from typing import Callable + + +def make_point(x: int, y: int) -> Callable: + def point() -> None: print(f"Point({x}, {y})") - def get_x(): + def get_x() -> int: return x - def get_y(): + def get_y() -> int: return y - def set_x(value): + def set_x(value: int) -> None: nonlocal x x = value - def set_y(value): + def set_y(value: int) -> None: nonlocal y y = value # Attach getters and setters - point.get_x = get_x - point.set_x = set_x - point.get_y = get_y - point.set_y = set_y + point.get_x = get_x # type: ignore[attr-defined] + point.set_x = set_x # type: ignore[attr-defined] + point.get_y = get_y # type: ignore[attr-defined] + point.set_y = set_y # type: ignore[attr-defined] return point if __name__ == "__main__": point = make_point(1, 2) - print(point.get_x()) - print(point.get_y()) + print(point.get_x()) # type: ignore[attr-defined] + print(point.get_y()) # type: ignore[attr-defined] point() - - point.set_x(42) - point.set_y(7) + + point.set_x(42) # type: ignore[attr-defined] + point.set_y(7) # type: ignore[attr-defined] point() diff --git a/closures/text_printer.py b/closures/text_printer.py index 98be8fe137e90e957b019b417ca5be2bf6f698a3..a106efdbb6572104ac04ddd471e6e3363411bffd 100644 --- a/closures/text_printer.py +++ b/closures/text_printer.py @@ -1,7 +1,10 @@ -def print_msg(msg): +from typing import Callable + + +def print_msg(msg: str) -> Callable: # This is the outer enclosing function - def printer(): + def printer() -> None: # This is the nested function print(msg) diff --git a/context_managers/async_context_manager_usage_sample.py b/context_managers/async_context_manager_usage_sample.py index 6f8086ee333c1273f07449678231ae70c20bb68e..718efa755ac4e604c38831fdde0e292aaad2fe8a 100644 --- a/context_managers/async_context_manager_usage_sample.py +++ b/context_managers/async_context_manager_usage_sample.py @@ -1,17 +1,21 @@ -import aiohttp import asyncio -async def check(url): +import aiohttp + + +async def check(url: str) -> None: async with aiohttp.ClientSession() as session: async with session.get(url) as response: print(f"{url}: status -> {response.status}") html = await response.text() print(f"{url}: type -> {html[:17].strip()}") -async def main(): + +async def main() -> None: await asyncio.gather( check("https://realpython.com"), check("https://pycoders.com"), ) -asyncio.run(main()) \ No newline at end of file + +asyncio.run(main()) diff --git a/context_managers/asynchronous_context_manager.py b/context_managers/asynchronous_context_manager.py index 4ef3221fe207609550c2f1f18d2a76b87396f33b..97e9b4a75fea8c596a3fc5bdcf6bcfa0d61cac9f 100644 --- a/context_managers/asynchronous_context_manager.py +++ b/context_managers/asynchronous_context_manager.py @@ -1,28 +1,31 @@ -import aiohttp import asyncio +from typing import Any + +import aiohttp +from aiohttp import ClientResponse class AsyncSession: - def __init__(self, url): + def __init__(self, url: str) -> None: self._url = url - async def __aenter__(self): + async def __aenter__(self) -> ClientResponse: self.session = aiohttp.ClientSession() response = await self.session.get(self._url) return response - async def __aexit__(self, exc_type, exc_value, exc_tb): + async def __aexit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: await self.session.close() -async def check(url): +async def check(url: str) -> None: async with AsyncSession(url) as response: print(f"{url}: status -> {response.status}") html = await response.text() print(f"{url}: type -> {html[:17].strip()}") -async def main(): +async def main() -> None: await asyncio.gather( check("https://realpython.com"), check("https://pycoders.com"), diff --git a/context_managers/class_based_context_managers.py b/context_managers/class_based_context_managers.py index 4b8ae6ccc70937ecca9e3cdb88c0496ef5bf6094..50ad327ed6ab64b7e980a0041163706b80d57d95 100644 --- a/context_managers/class_based_context_managers.py +++ b/context_managers/class_based_context_managers.py @@ -1,12 +1,14 @@ +from collections import Callable from time import perf_counter, sleep +from typing import Any class HelloContextManager: - def __enter__(self): + def __enter__(self) -> str: print("Entering the context...") return "Hello, World!" - def __exit__(self, exc_type, exc_value, exc_tb): + def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> Any: print("Leaving the context...") # handle IndexError here... if isinstance(exc_value, IndexError): @@ -16,12 +18,12 @@ class HelloContextManager: class TimerContextManager: - def __enter__(self): + def __enter__(self) -> Callable: self.start = perf_counter() self.end = 0.0 return lambda: self.end - self.start - def __exit__(self, *args): + def __exit__(self, *args: list) -> None: self.end = perf_counter() diff --git a/context_managers/context_managers_usage_samples.py b/context_managers/context_managers_usage_samples.py index 1643017453a137555ad82979f5b0ca44352be91e..c0bd67fdd26450475a4bd9f5a6feb8f5f79235bd 100644 --- a/context_managers/context_managers_usage_samples.py +++ b/context_managers/context_managers_usage_samples.py @@ -1,6 +1,5 @@ import os import pathlib -import pytest import random import threading import time @@ -8,6 +7,8 @@ from decimal import Decimal, localcontext from threading import Lock from typing import Callable +import pytest + print("==========================================================") # clear folder from existing files if they present try: @@ -46,7 +47,7 @@ x = 0 lock = Lock() -def increment_with_lock(increment_by, lock): +def increment_with_lock(increment_by: int, lock: Lock) -> None: global x with lock: x += increment_by @@ -54,14 +55,14 @@ def increment_with_lock(increment_by, lock): print(f"{threading.current_thread().name} increments x by {increment_by}, x: {x}\n") -def increment_without_lock(increment_by): +def increment_without_lock(increment_by: int) -> None: global x x += increment_by time.sleep(1) print(f"{threading.current_thread().name} increments x by {increment_by}, x: {x}\n") -def call_threads(func: Callable, lock: Lock = None): +def call_threads(func: Callable, lock: Lock = None) -> None: threads = [] for i in range(5): thread = threading.Thread( diff --git a/context_managers/function_based_context_managers.py b/context_managers/function_based_context_managers.py index f086a1285733c782fa1997090b21dcaae44e7877..b0106a32de10ec8e1a4852a34ad2070459be6c40 100644 --- a/context_managers/function_based_context_managers.py +++ b/context_managers/function_based_context_managers.py @@ -1,10 +1,10 @@ from contextlib import contextmanager - from time import perf_counter, sleep +from typing import Generator @contextmanager -def hello_context_manager(): +def hello_context_manager() -> Generator: print("Entering the context...") try: yield "Hello, World!" @@ -18,7 +18,7 @@ def hello_context_manager(): @contextmanager -def timer_context_manager(): +def timer_context_manager() -> Generator: start = perf_counter() end = 0.0 yield lambda: end - start @@ -32,11 +32,11 @@ try: hello[100] except IndexError as ex: print("We reraised an exception that occurred in context manager!") - print(ex) + print(ex) @hello_context_manager() -def use_hello_context_manager(): +def use_hello_context_manager() -> None: print(hello) diff --git a/dataclasses/frozen.py b/dataclasses/frozen.py index dc70d212d5987faa1f4e23142e0ecf2fbaa96cb6..6ceb82a3f76bcd2f1e5292f23d37e9c73e18b489 100644 --- a/dataclasses/frozen.py +++ b/dataclasses/frozen.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass, FrozenInstanceError +from dataclasses import FrozenInstanceError, dataclass @dataclass(frozen=True) @@ -21,7 +21,7 @@ if __name__ == "__main__": frozen_all = TotallyFrozen() print("We can't modify frozen objects:") try: - frozen_heart.is_your_heart_frozen = False + frozen_heart.is_your_heart_frozen = False # type: ignore except FrozenInstanceError as ex: print(ex) try: diff --git a/dataclasses/person.py b/dataclasses/person.py index b59c040fb37af359ad5267d23c0e0c3ff7cc2330..a5de3e0448df42e1272a0dee560e3967f9e5fcfd 100644 --- a/dataclasses/person.py +++ b/dataclasses/person.py @@ -1,5 +1,5 @@ -from dataclasses import dataclass, astuple, asdict, field, InitVar -from typing import Any, List, ClassVar, Set, Optional +from dataclasses import InitVar, asdict, astuple, dataclass, field +from typing import Any, ClassVar, List, Optional, Set @dataclass(order=True) @@ -10,7 +10,9 @@ class Person: hobbies: List[str] = field( default_factory=list, metadata={"description": "list of hobbies"} - ) # correct way of providing default values for immutable fields; metadata is used for attaching some info to fields, not used directly by dataclass itself + ) + # correct way of providing default values for immutable fields; + # metadata is used for attaching some info to fields, not used directly by dataclass itself job: str = "Python developer" # default value; fields with default values must be at the end notes: Any = None # when we don't want explicitly assign types @@ -24,8 +26,8 @@ class Person: # class attribute, shared by all instances of the class, use specific ClassVar annotation all_emails: ClassVar[Set[str]] = set() - answer_to_the_main_question = 42 # class attibute, 's'e there is no annotation - + answer_to_the_main_question = 42 # class attibute, 's'e there is no annotation + # initialization variable that is not part of the class, but is used by the class methods company_name: InitVar[str] = "Dunder Mifflin" @@ -37,7 +39,9 @@ class Person: # check for email uniqueness cls = self.__class__ if self.email is None: - self.email = f"{self.first_name[0].lower()}{self.last_name.lower()}@{domain}.com" + self.email = ( + f"{self.first_name[0].lower()}{self.last_name.lower()}@{domain}.com" + ) if self.email in cls.all_emails: raise ValueError( @@ -70,6 +74,6 @@ if __name__ == "__main__": print("Person is the same as than Person2:", person == person2) print("Person1 is younger than Person2:", person1 < person2) - + print(person1.answer_to_the_main_question) print(person2.answer_to_the_main_question) diff --git a/dataclasses/position.py b/dataclasses/position.py index 3954f3b236ffc64f15bb3359057237d5ab0076b9..c4fa0ea3f13873e05e27b957c7dcd44d9c17709f 100644 --- a/dataclasses/position.py +++ b/dataclasses/position.py @@ -1,7 +1,8 @@ -from dataclasses import dataclass, asdict -from pympler import asizeof +from dataclasses import asdict, dataclass from timeit import timeit +from pympler import asizeof + @dataclass class SimplePosition: @@ -19,7 +20,7 @@ class SlotPosition: lat: float -@dataclass(slots=True) +@dataclass(slots=True) # type: ignore class SlotPosition2: # starting from 3.10 slots are available as argument name: str @@ -27,7 +28,7 @@ class SlotPosition2: lat: float -@dataclass(kw_only=True) +@dataclass(kw_only=True) # type: ignore class KeywordOnlyPosition: # keyword-only argument is available since 3.10 name: str diff --git a/dunders/.DS_Store b/dunders/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..63b3508f4db8654ba7dfa49e6a6f7d7d8b156b61 Binary files /dev/null and b/dunders/.DS_Store differ diff --git a/dunders/attributes.py b/dunders/attributes.py new file mode 100644 index 0000000000000000000000000000000000000000..57a56b43147cc0c658ccb259d099b8580132f891 --- /dev/null +++ b/dunders/attributes.py @@ -0,0 +1,43 @@ +from typing import Any + + +class A: + """Create attribute if it starts with new""" + + def __getattr__(self, key: str) -> str: + if key.startswith("new"): + self.__dict__[key] = "I'm new attribute" + return "New attribute added" + else: + raise AttributeError("No such attribute") + + +class B: + """Raise an error if attribute name starts with hidden""" + + hidden_and_dangerous = 2 + dangerous = 3 + + def __getattribute__(self, item: str) -> Any: + if item.startswith("hidden"): + raise AttributeError("Could not get hidden attribute") + return object.__getattribute__(self, item) + + +if __name__ == "__main__": + a = A() + try: + print(a.asd) + except Exception as ex: + print(ex) + print("New attr defined") + print(a.new_asd) # Get None, __getattr__ called after __getattribute__ + print("Get new attribute") + print(a.new_asd) # Get new attribute + + b = B() + print(b.dangerous) + try: + print(b.hidden_and_dangerous) + except Exception as ex: + print(ex) diff --git a/dunders/dunders.py b/dunders/dunders.py index 63b0b4e6d06b9e88a3777017880e20f9df333797..71d5aecce462eac5c44eebb96abba65d3efdcc05 100644 --- a/dunders/dunders.py +++ b/dunders/dunders.py @@ -1,99 +1,47 @@ import random +from typing import Any class Class1: - def __init__(self): + def __init__(self) -> None: print("Class of type '1' initialization") class Class2: - def __init__(self): + def __init__(self) -> None: print("Class of type '2' initialization") class NewClass: providers = {"1": Class1, "2": Class2} - def __new__(cls, type, *args, **kwargs): - if not type in cls.providers: - raise Exception( - f"Creation an instance of {type=} class is forbidden.") - provider = cls.providers.get(type) - print(f"Creating an instance of {type=} class") + def __new__(cls: Any, class_type: str, *args: list, **kwargs: dict) -> Any: + if type not in cls.providers: + raise TypeError(f"Creation an instance of {class_type} class is forbidden.") + provider = cls.providers.get(class_type) + print(f"Creating an instance of {class_type} class") return provider(*args, **kwargs) class ListRandomizer: __slots__ = ["val_list"] - def __init__(self, val_list: list): + def __init__(self, val_list: list) -> None: self.val_list = val_list - def __len__(self): + def __len__(self) -> int: return len(self.val_list) - def __repr__(self): + def __repr__(self) -> str: return f"Class to return one of values from list {self.val_list}" - def __str__(self): + def __str__(self) -> str: return str(self.val_list) - def __call__(self): + def __call__(self) -> int: return random.choice(self.val_list) -class A: - """Can be iterated""" - - def __getitem__(self, index): - if index >= 10: - raise IndexError - return index - - -class B: - """Can be iterated""" - - def __iter__(self): - for i in range(10): - yield i - - -class B1: - """Cannot be iterated""" - - def __iter__(self): - return None - - def __getitem__(self, index): - if index >= 10: - raise IndexError - return index - - -class C: - """Functions as an iterator""" - - def __iter__(self): - self.num = 0 - return self - - def __next__(self): - if self.num < 10: - num = self.num - self.num += 1 - return num - else: - raise StopIteration - - -class D: - def __getattr__(self, key): - if key in self._data: - return self._data.get(key) - raise AttributeError(key) - - if __name__ == "__main__": # creating allowed classes types new_class = NewClass("1") @@ -109,11 +57,3 @@ if __name__ == "__main__": print(repr(list_randomizer)) print(len(list_randomizer)) print(list_randomizer()) - # __getitem__, __iter__, __next__ - print(list(A())) - print(list(B())) - print(list(C())) - try: - print(list(B1())) - except TypeError as ex: - print(ex) diff --git a/dunders/iterators.py b/dunders/iterators.py new file mode 100644 index 0000000000000000000000000000000000000000..e907695eb9c5a3bf2a849acd5efa14b08216c2f6 --- /dev/null +++ b/dunders/iterators.py @@ -0,0 +1,58 @@ +from typing import Any, Generator + + +class A: + """Can be iterated""" + + def __getitem__(self, index: int) -> int: + if index >= 10: + raise IndexError + return index + + +class B: + """Can be iterated""" + + def __iter__(self) -> Generator: + for i in range(10): + yield i + + +class B1: + """Cannot be iterated""" + + def __iter__(self) -> Any: + return None + + def __getitem__(self, index: int) -> int: + if index >= 10: + raise IndexError + return index + + +class C: + """Functions as an iterator""" + + def __iter__(self) -> Any: + self.num = 0 + return self + + def __next__(self) -> int: + if self.num < 10: + num = self.num + self.num += 1 + return num + else: + raise StopIteration + + +if __name__ == "__main__": + a = A() + + print(list(A())) # type: ignore + print(list(B())) + print(list(C())) + try: + print(list(B1())) + except TypeError as ex: + print(ex) diff --git a/encapsulation/encapsulation.py b/encapsulation/encapsulation.py index 55ee0e5527e568cff823f29e9fb929256cc5e71b..57673e7db97bb817030f83bf1cfe4f22e6449cb3 100644 --- a/encapsulation/encapsulation.py +++ b/encapsulation/encapsulation.py @@ -3,22 +3,22 @@ class Encapsulation: _protected_value = 1 __private_value = 2 - def public_method(self): + def public_method(self) -> None: self.public_value += 1 self._protected_value += 1 self.__private_value += 1 - def _protected_method(self): + def _protected_method(self) -> None: self.public_value -= 1 self._protected_value -= 1 self.__private_value -= 1 - def __private_method(self): + def __private_method(self) -> None: print(self.public_value) print(self._protected_value) print(self.__private_value) - def unprivate(self): + def unprivate(self) -> None: self.__private_method() @@ -34,30 +34,29 @@ if __name__ == "__main__": # protected value, accessible in inheritors print(e._protected_value) # private with name mangling, expected to be used only inside a class - print(e._Encapsulation__private_value) + print(e._Encapsulation__private_value) # type: ignore print("_____________________________") # public method to use outside of class e.public_method() # private method, expected to be used only inside a class - e._Encapsulation__private_method() + e._Encapsulation__private_method() # type: ignore print("_____________________________") # not normal but still possible e._protected_method() - e._Encapsulation__private_method() + e._Encapsulation__private_method() # type: ignore + print("_____________________________") e.unprivate() - print( - "Crack inheritance from Encapsulation" - ) + print("Crack inheritance from Encapsulation") c = Cracker() print(c.public_value) print(c._protected_value) - print(c._Encapsulation__private_value) + print(c._Encapsulation__private_value) # type: ignore[attr-defined] print("_____________________________") c.public_method() - c._Encapsulation__private_method() + c._Encapsulation__private_method() # type: ignore print("_____________________________") c._protected_method() - c._Encapsulation__private_method() + c._Encapsulation__private_method() # type: ignore print("_____________________________") c.unprivate() diff --git a/inheritance/MRO_example.py b/inheritance/MRO_example.py index 137585c598efc23ae1957e8e075ba94693b36920..49e8a9c629c8ba5fb00651f9c6fd427ad0bd16ea 100644 --- a/inheritance/MRO_example.py +++ b/inheritance/MRO_example.py @@ -1,25 +1,69 @@ -class O: ... -class A(O): ... -class B(O): ... -class C(O): ... -class D(O): ... -class E(O): ... -class K1(A, B, C): ... -class K2(B, D): ... -class K3(C, D, E): ... -class Z(K1, K2, K3): ... - - -class X: ... -class Y: ... -class A1(X, Y): ... -class B1(Y, X): ... +from typing import List + + +class O: # noqa: E742 + pass + + +class A(O): + ... + + +class B(O): + ... + + +class C(O): + ... + + +class D(O): + ... + + +class E(O): + ... + + +class K1(A, B, C): + ... + + +class K2(B, D): + ... + + +class K3(C, D, E): + ... + + +class Z(K1, K2, K3): + ... + + +class X: + ... + + +class Y: + ... + + +class A1(X, Y): + ... + + +class B1(Y, X): + ... + class MyMRO(type): # inherited from type metaclass - def mro(cls): - return (cls, A1, B1, X, Y, object) # define needed order + def mro(cls) -> List[type]: + return [cls, A1, B1, X, Y, object] # define needed order + -class G(A, B, metaclass=MyMRO): ... +class G(A, B, metaclass=MyMRO): + ... if __name__ == "__main__": diff --git a/inheritance/inheritance_examples.py b/inheritance/inheritance_examples.py index 4f77625e0bad4cdb4c2a1ca4dfb852f8cd097d62..9f58cff9e0428893b1789bf193eb81e69c5784e8 100644 --- a/inheritance/inheritance_examples.py +++ b/inheritance/inheritance_examples.py @@ -2,38 +2,38 @@ from collections import UserDict class Root: - def ping(self): + def ping(self) -> None: print(f"{self}.ping() in Root") - def pong(self): + def pong(self) -> None: print(f"{self}.pong() in Root") - def __repr__(self): + def __repr__(self) -> str: cls_name = type(self).__name__ return f"" class A(Root): - def ping(self): + def ping(self) -> None: print(f"{self}.ping() in A") super().ping() - def pong(self): + def pong(self) -> None: print(f"{self}.pong() in A") super().pong() class B(Root): - def ping(self): + def ping(self) -> None: print(f"{self}.ping() in B") super().ping() - def pong(self): + def pong(self) -> None: print(f"{self}.pong() in B") class Leaf(A, B): - def ping(self): + def ping(self) -> None: print(f"{self}.ping() in Leaf") super().ping() @@ -47,26 +47,26 @@ print("==========================================================") class DoubleDictBad(dict): - def __setitem__(self, key, value): + def __setitem__(self, key: str, value: int) -> None: super().__setitem__(key, [value] * 2) -dd = DoubleDictBad(one=1) # {'one': 1} -print(dd) -dd["two"] = 2 # {'one': 1, 'two': [2, 2]} -print(dd) -dd.update({"three": 3}) # {'one': 1, 'two': [2, 2], 'three': 3} -print(dd) +dd_bad = DoubleDictBad(one=1) # {'one': 1} +print(dd_bad) +dd_bad["two"] = 2 # {'one': 1, 'two': [2, 2]} +print(dd_bad) +dd_bad.update({"three": 3}) # {'one': 1, 'two': [2, 2], 'three': 3} +print(dd_bad) class DoubleDictGood(UserDict): - def __setitem__(self, key, value): + def __setitem__(self, key: str, value: int) -> None: super().__setitem__(key, [value] * 2) -dd = DoubleDictGood(one=1) # {'one': [1, 1]} -print(dd) -dd["two"] = 2 # {'one': [1, 1], 'two': [2, 2]} -print(dd) -dd.update({"three": 3}) # {'one': [1, 1], 'two': [2, 2], 'three': [3, 3]} -print(dd) +dd_good = DoubleDictGood(one=1) # {'one': [1, 1]} +print(dd_good) +dd_good["two"] = 2 # {'one': [1, 1], 'two': [2, 2]} +print(dd_good) +dd_good.update({"three": 3}) # {'one': [1, 1], 'two': [2, 2], 'three': [3, 3]} +print(dd_good) diff --git a/lambdas/samples.py b/lambdas/samples.py index 93f022f4ad4862c1b64678c22f229d2ed7351669..4e02c01892a6034f582caa73ae110f36c4e39b70 100644 --- a/lambdas/samples.py +++ b/lambdas/samples.py @@ -1,30 +1,31 @@ from functools import reduce -from timeit import timeit from math import factorial +from timeit import timeit +from typing import Callable -add_one = lambda x: x + 1 +add_one = lambda x: x + 1 # noqa: E731 -def add_two(a, b): +def add_two(a: int, b: int) -> int: return a + b -def trace(f): - def wrap(*args, **kwargs): +def trace(f: Callable) -> Callable: + def wrap(*args: list, **kwargs: dict) -> Callable: print(f"[TRACE] func: {f.__name__}, args: {args}, kwargs: {kwargs}") return f(*args, **kwargs) return wrap -def to_be_patched(): +def to_be_patched() -> None: print("to be patched") if __name__ == "__main__": # immediately invoked function print((lambda x: x + 1)(2)) - print(add_one(2)) + print(add_one(2)) # type: ignore # lamdba function is anonimous and is seen as # if there is an exceptions in lambda, it will be seen as in traceback @@ -34,8 +35,8 @@ if __name__ == "__main__": # samples of different supported arguments print((lambda: 6)()) print((lambda x, y, z: x + y + z)(1, 2, 3)) - print((lambda x, y, z=3: x + y + z)(1, 2)) - print((lambda x, y, z=3: x + y + z)(1, y=2)) + print((lambda x, y, z=3: x + y + z)(1, 2)) # type: ignore + print((lambda x, y, z=3: x + y + z)(1, y=2)) # type: ignore print((lambda *args: sum(args))(1, 2, 3)) print((lambda **kwargs: sum(kwargs.values()))(one=1, two=2, three=3)) print((lambda x, *, y=0, z=0: x + y + z)(1, y=2, z=3)) @@ -56,6 +57,6 @@ if __name__ == "__main__": print(timeit("factorial(999)", "from math import factorial", number=10)) print(timeit(lambda: factorial(999), number=10)) - # monkey patchting - to_be_patched = lambda: print("patched") - to_be_patched() + # monkey patching + monkey_patchting = lambda: print("patched") # noqa: E731, F811 + monkey_patchting() # type: ignore diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000000000000000000000000000000000000..f8b964e5c93ab73f0fbbcd39a570054eae570194 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,55 @@ +import nox +import os +from os.path import isfile +import pkg_resources +import platform +from typing import List + + +files: List[str] = [] +for file in os.listdir(): + if ( + not isfile(file) + and not file.startswith(".") + and file not in ["venv", "__pycache__"] + ): + files.append(file) +install_requires: List[str] = ["pip", "install", "-r", "requirements.txt"] + +python_version_info = platform.sys.version_info +# if python_version_info.major < 3 or python_version_info.minor < 9: +# raise Exception("Python version should be 3.9 or higher") + + +@nox.session(name="requirements") +def install_req(session: nox.session) -> None: + """Check requirements""" + with open("requirements.txt", "r") as f: + try: + pkg_resources.require(f.read()) + except pkg_resources.DistributionNotFound: + session.run(*install_requires) + + +@nox.session(name="isort") +def isort(session: nox.session) -> None: + """Run isort import sorter.""" + session.run(session.name, *files, external=True) + + +@nox.session(name="black") +def black(session: nox.session) -> None: + """Run black code formatter.""" + session.run(session.name, *files, external=True) + + +@nox.session(name="flake8") +def flake8(session: nox.session) -> None: + """Run flake8 linter.""" + session.run(session.name, *files, external=True) + + +@nox.session(name="mypy") +def mypy(session: nox.session) -> None: + """Run mypy linter.""" + session.run(session.name, *files, external=True) diff --git a/polymorphism/polymorphism.py b/polymorphism/polymorphism.py new file mode 100644 index 0000000000000000000000000000000000000000..a185ec220c55702f4610f7b3cd4e1df7531bf254 --- /dev/null +++ b/polymorphism/polymorphism.py @@ -0,0 +1,40 @@ +from typing import Any + + +class Dog: + # Doc sound + def sound(self) -> None: + print("auf") + + +class Cat: + # Cat sound, same definition but other result, polymorphic method + def sound(self) -> None: + print("myauf") + + +class A: + def __init__(self, val: int) -> None: + self.val = val + + def __add__(self, other: Any) -> int: + return self.val - other.val + + +class B: + def __init__(self, val: str) -> None: + self.val = val + + def __add__(self, other: Any) -> str: + return self.val + other.val + + +if __name__ == "__main__": + Dog().sound() + Cat().sound() + a1 = A(2) + a2 = A(3) + print(a1 + a2) + b1 = B("Hello ") + b2 = B("world!") + print(b1 + b2) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..c4070d8079c1bcc9b15105cfe90daf8a5c5ee5a6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +black==22.6.0 +isort==5.10.1 +flake8==5.0.3 +mypy==0.971 +nox +aiohttp +pytest +pympler \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..343ee3181d06a1c3cb5913e49f1fc6740aa81a8a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,29 @@ +[isort] +line_length = 115 +default_section = LOCALFOLDER +multi_line_output = 5 + +[flake8] +max-line-length = 120 +max-doc-length = 200 + +[black] +line-length = 120 + +[mypy] +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +check_untyped_defs = true +disallow_untyped_decorators = false +warn_redundant_casts = true +warn_unused_ignores = true +warn_unreachable = true +show_error_context = true +show_column_numbers = true +show_error_codes = true +pretty = true +warn_incomplete_stub = true +scripts_are_modules = false +warn_unused_configs = true +ignore_missing_imports = True