Metadata-Version: 2.1
Name: accessify
Version: 0.3.1
Summary: Python design kit: interfaces, declared exception throws, class members accessibility levels (private and protected methods for humans).
Home-page: https://github.com/dmytrostriletskyi/accessify
Author: Dmytro Striletskyi
Author-email: dmytro.striletskyi@gmail.com
License: MIT
Description: # accessify
        
        [![Release](https://img.shields.io/github/release/dmytrostriletskyi/accessify.svg)](https://github.com/dmytrostriletskyi/accessify/releases)
        [![PyPI version shields.io](https://img.shields.io/pypi/v/accessify.svg)](https://pypi.python.org/pypi/accessify/)
        [![Build Status](https://travis-ci.com/dmytrostriletskyi/accessify.svg?branch=develop)](https://travis-ci.com/dmytrostriletskyi/accessify)
        [![codecov](https://codecov.io/gh/dmytrostriletskyi/design-kit/branch/develop/graph/badge.svg)](https://codecov.io/gh/dmytrostriletskyi/design-kit)
        
        [![Downloads](https://pepy.tech/badge/accessify)](https://pepy.tech/project/accessify)
        [![PyPI license](https://img.shields.io/pypi/l/accessify.svg)](https://pypi.python.org/pypi/accessify/)
        [![PyPI pyversions](https://img.shields.io/pypi/pyversions/accessify.svg)](https://pypi.python.org/pypi/accessify/)
        
        [![Habrahabr](https://img.shields.io/badge/Post-Habrahabr-brightgreen.svg)](https://habr.com/ru/post/443192/)
        
          * [Getting started](#getting-started)
            * [What is accessify](#what-is-accessify)
            * [Access modifiers](#getting-started-access-modifiers)
              * [Motivation](#getting-started-access-modifiers-motivation)
            * [Interfaces](#getting-started-interfaces)
              * [Motivation](#getting-started-interfaces-motivation)
            * [How to install](#how-to-install)
          * [Usage](#usage)
            * [Access modifiers](#usage-access-modifiers)
              * [Private](#private)
              * [Protected](#protected)
              * [Other features](#other-features)
            * [Interfaces](#usage-interfaces)
              * [Single interface](#single-interface)
              * [Multiple interfaces](#multiple-interfaces)
              * [Exception throws declaration](#exception-throws-declaration)
          * [Disable checking](#disable-checking)
          * [Contributing](#contributing)
          * [References](#references)
        
        ## Getting started
        
        ### What is accessify
        
        `accessify` is a `Python` design kit that provides:
        * interfaces, 
        * declared exceptions throws, 
        * class members accessibility levels.
        
        that could be combined with each other to make your code slim and this library usage more justified.
        
        <h3 id="getting-started-access-modifiers">Access modifiers</h3>
        
        Access level modifiers determine whether other classes can use a particular field or invoke a particular method.
        Accessibility levels are presented from the box in the languages like `C++`, `C#` and `Java`. 
        
        ```csharp
        class Car 
        {
            private string StartEngine()
            {
                // Code here.
            }
        }
        ```
        
        But `Python` does not have this in the same way.
        
        <h4 id="getting-started-access-modifiers-motivation">Motivation</h4>
        
        * `We're all consenting adults here` that is the  part of the `Python philosophy` that relies on human factor instead of the interpreter.
        * There is a `Python convention` that is to use an underscore prefix for protected and private members, that is a bit ugly. 
        Isn't it? For instance, for the following piece of code that provides class a private member.
        
        ```python
        class Car:
        
            def __start_engine(self, *args, **kwargs):
                pass
        ```
        
        * Moreover, private and protected methods could be easily accessed outside the class. This is really a point to postpone the 
        correct design of the system to the backlog, increasing the technical debt.
        
         ```python
        class Car:
        
            def _start_engine(self, *args, **kwargs):
                pass
                
            def __start_engine(self, *args, **kwargs):
                pass
        
        
        car = Car()
        car._start_engine()
        car._Car__start_engine()
        ```
        
        <h3 id="getting-started-interfaces">Interfaces</h3>
        
        An interface is a contract specifying a set of methods and properties which required to be available on any implementing class.
        If the class implements an interface, but does not realize its method, corresponding errors should be raised. Interfaces are presented from the box in 
        the languages like `C++`, `C#` and `Java`. 
        
        ```csharp
        interface HumanInterface
        {
            public string EatFood();
        }
        
        class Human : HumanInterface
        {
            public string EatFood()
            {
                // Code here.
            }
        }
        ```
        
        But `Python` does not have this in the same way.
        
        <h4 id="getting-started-interfaces-motivation">Motivation</h4>
        
        * The interface makes checks during the implementation creation, but not actually while execution like [abc](https://docs.python.org/3/library/abc.html) module in `Python`.
        * The interface requires that implementation's method arguments match with arguments declared in interfaces, [abc](https://docs.python.org/3/library/abc.html) — not.
        * A lot of libraries that provide interfaces are no longer supported.
        * A lot of libraries that provide interfaces require you to write a lot of code to use its functionality, this library — not.
        
        ### How to install
        
        Using [pip](https://pypi.org/project/pip) install the package from the [PyPi](https://pypi.org/project/accessify).
        
        ```bash
        $ pip3 install accessify
        ```
        
        ## Usage
        
        <h3 id="usage-access-modifiers">Access modifiers</h3>
        
        #### Private
        
        * Private members are accessible only within the body of the class.
        
        In this example, the `Car` class contains a private member named `start_engine`. As a private member, they cannot be accessed
        except by member methods. The private member `start_engine` is accessed only by way of a public method called `run`. 
        
        ```python
        from accessify import private
        
        
        class Car:
        
            @private
            def start_engine(self):
                return 'Engine sound.'
        
            def run(self):
                return self.start_engine()
        
        
        if __name__ == '__main__':
            car = Car()
        
            assert 'Engine sound.' == car.run()
        
            car.start_engine()
        ```
        
        The code above will produce the following traceback.
        
        ```bash
        Traceback (most recent call last):
          File "examples/access/private.py", line 24, in <module>
            car.start_engine()
          File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/main.py", line 92, in private_wrapper
            class_name=instance_class.__name__, method_name=method.__name__,
        accessify.errors.InaccessibleDueToItsProtectionLevelException: Car.start_engine() is inaccessible due to its protection level
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhASP > private.py
        $ python3 private.py
        ```
        
        * Child classes cannot access parent private members.
        
        In this example, the `Car` class contains a private member named `start_engine`. As a private member, they cannot be accessed
        from the child classes, `Tesla` in our case. So overridden method `run` by `Tesla` class cannot use the parent's `start_engine` member.
        
        ```python
        from accessify import private
        
        
        class Car:
        
            @private
            def start_engine(self):
                return 'Engine sound.'
        
        
        class Tesla(Car):
        
            def run(self):
                return self.start_engine()
        
        
        if __name__ == '__main__':
            tesla = Tesla()
            tesla.run()
        ```
        
        The code above will produce the following traceback.
        
        ```bash
        Traceback (most recent call last):
          File "examples/inheritance/private.py", line 23, in <module>
            tesla.run()
          File "examples/inheritance/private.py", line 18, in run
            return self.start_engine()
          File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/main.py", line 94, in private_wrapper
            class_name=class_contain.__name__, method_name=method.__name__,
        accessify.errors.InaccessibleDueToItsProtectionLevelException: Car.start_engine() is inaccessible due to its protection level
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhASX > inheritence_private.py
        $ python3 inheritence_private.py
        ```
        
        #### Protected
        
        * A protected member is accessible within its class and by derived class instances.
        
        In this example, the `Car` class contains a protected member named `start_engine`. As a protected member, they cannot be accessed
        except by member methods. The protected member `start_engine` is accessed only by way of a public method called `run`. 
        
        ```python
        from accessify import protected
        
        
        class Car:
        
            @protected
            def start_engine(self):
                return 'Engine sound.'
        
            def run(self):
                return self.start_engine()
        
        
        if __name__ == '__main__':
            car = Car()
        
            assert 'Engine sound.' == car.run()
        
            car.start_engine()
        ```
        
        The code above will produce the following traceback.
        
        ```bash
        Traceback (most recent call last):
          File "examples/access/protected.py", line 21, in <module>
            car.start_engine()
          File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/main.py", line 134, in protected_wrapper
            class_name=instance_class.__name__, method_name=method.__name__,
        accessify.errors.InaccessibleDueToItsProtectionLevelException: Car.start_engine() is inaccessible due to its protection level
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhASM > protected.py
        $ python3 protected.py
        ```
        
        * Child classes have access to those protected members.
        
        In this example, the `Car` class contains a protected member named `start_engine`. As a protected member, they can be accessed
        from the child classes, `Tesla` in our case. So overridden method `run` by `Tesla` class can use the parent's `start_engine` member.
        
        
        ```python
        from accessify import protected
        
        
        class Car:
        
            @protected
            def start_engine(self):
                return 'Engine sound.'
        
        
        class Tesla(Car):
        
            def run(self):
                return self.start_engine()
        
        
        if __name__ == '__main__':
            tesla = Tesla()
        
            assert 'Engine sound.' == tesla.run()
        ```
        
        The code will work without errors.
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhASD > inheritence_protected.py
        $ python3 inheritence_protected.py
        ```
        
        #### Other features
        
        * The `accessify` decorator removes private and protected members from class [dir](https://docs.python.org/3/library/functions.html#dir).
        
        ```python
        from accessify import accessify, private
        
        
        @accessify
        class Car:
        
            @private
            def start_engine(self):
                return 'Engine sound.'
        
        if __name__ == '__main__':
            car = Car()
        
            assert 'start_engine' not in dir(car)
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhASy > dir.py
        $ python3 dir.py
        ```
        
        <h3 id="usage-interfaces">Interfaces</h3>
        
        #### Single interface
        
        * When you declare that class implements an interface, a class should implement **all methods** presented in the interface.
        
        In this example, there is an interface called `HumanInterface` that contains two methods `love` and `eat`. Also, there is
        a class `Human` that implements the interface but **missed method «eat»**, so the corresponding error should be raised.
        
        ```python
        from accessify import implements
        
        
        class HumanInterface:
        
            @staticmethod
            def eat(food, *args, allergy=None, **kwargs):
                pass
        
        
        if __name__ == '__main__':
        
            @implements(HumanInterface)
            class Human:
        
                pass
        ```
        
        The code above will produce the following traceback.
        
        ```bash
        Traceback (most recent call last):
          File "examples/interfaces/single.py", line 18, in <module>
            @implements(HumanInterface)
          File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 66, in decorator
            interface_method_arguments=interface_method.arguments_as_string,
        accessify.errors.InterfaceMemberHasNotBeenImplementedException: class Human does not implement interface member HumanInterface.eat(food, args, allergy, kwargs)
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhh2V > single_method.py
        $ python3 single_method.py
        ```
        
        * When you declare that class implements an interface, a class should implement all methods that presented in the interface 
        including **number, order and naming of the accepting arguments**. 
        
        In this example, there is an interface called `HumanInterface` that contains two methods `love` and `eat`. Also, there is
        a class `Human` that implements the interface but **missed 3 of 4 arguments for method «eat»**, so the corresponding error should be raised.
        
        ```python
        from accessify import implements
        
        
        class HumanInterface:
        
            @staticmethod
            def eat(food, *args, allergy=None, **kwargs):
                pass
        
        
        if __name__ == '__main__':
        
            @implements(HumanInterface)
            class Human:
        
                @staticmethod
                def eat(food):
                    pass
        ```
        
        The code above will produce the following traceback.
        
        ```bash
        Traceback (most recent call last):
          File "examples/interfaces/single_arguments.py", line 16, in <module>
            @implements(HumanInterface)
          File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 87, in decorator
            interface_method_arguments=interface_method.arguments_as_string,
        accessify.errors.InterfaceMemberHasNotBeenImplementedWithMismatchedArgumentsException: class Human implements interface member HumanInterface.eat(food, args, allergy, kwargs) with mismatched arguments
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhh2w > single_arguments.py
        $ python3 single_arguments.py
        ```
        
        * When you declare that class implements an interface, a class should implement all methods that presented in the interface 
        including number, order and naming of the accepting arguments and **access modifier type**.
        
        In this example, there is an interface called `HumanInterface` that contains two methods `love` and `eat`. Also, there is
        a class `Human` that implements the interface but **missed private access modifier type for method «eat»**, so the corresponding 
        error should be raised.
        
        ```python
        from accessify import implements, private
        
        
        class HumanInterface:
        
            @private
            @staticmethod
            def eat(food, *args, allergy=None, **kwargs):
                pass
        
        
        if __name__ == '__main__':
        
            @implements(HumanInterface)
            class Human:
        
                @staticmethod
                def eat(food, *args, allergy=None, **kwargs):
                    pass
        ```
        
        The code above will produce the following traceback.
        
        ```bash
        Traceback (most recent call last):
          File "examples/interfaces/single_access.py", line 18, in <module>
            @implements(HumanInterface)
          File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 77, in decorator
            interface_method_name=interface_method.name,
        accessify.errors.ImplementedInterfaceMemberHasIncorrectAccessModifierException: Human.eat(food, args, allergy, kwargs) mismatches HumanInterface.eat() member access modifier.
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhh2r > single_access.py
        $ python3 single_access.py
        ```
        
        #### Multiple interfaces
        
        * A class could implement multiple interfaces.
        * When you declare that class that implements a bunch of interfaces, a class should implement all method that presented in
        each interface including number, order and naming of the accepting arguments and access modifier type.
        
        In this example, there are an interface `HumanSoulInterface` that contains a method called `love` and interface `HumanBasicsInterface` that 
        contains a method called `eat`. Also, there is a class `Human` that implements method `love` from the first interface, but 
        **missed method «eat»** from the second one, so the corresponding error should be raised.
        
        ```python
        from accessify import implements
        
        
        class HumanSoulInterface:
        
            def love(self, who, *args, **kwargs):
                pass
        
        
        class HumanBasicsInterface:
        
            @staticmethod
            def eat(food, *args, allergy=None, **kwargs):
                pass
        
        
        if __name__ == '__main__':
        
            @implements(HumanSoulInterface, HumanBasicsInterface)
            class Human:
        
                def love(self, who, *args, **kwargs):
                    pass
        ```
        
        The code above will produce the following traceback.
        
        ```bash
        Traceback (most recent call last):
          File "examples/interfaces/multiple.py", line 19, in <module>
            @implements(HumanSoulInterface, HumanBasicsInterface)
          File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 66, in decorator
            interface_method_arguments=interface_method.arguments_as_string,
        accessify.errors.InterfaceMemberHasNotBeenImplementedException: class Human does not implement interface member HumanBasicsInterface.eat(food, args, allergy, kwargs)
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhh2o > multiple.py
        $ python3 multiple.py
        ```
        
        #### Exception throws declaration
        
        * When you declare that interface method throws a particular exception, a class method that implement interface should 
        contain code in the body that raise this exception.
        * You can declare that the interface method throws multiple exceptions.
        
        In this example, exception `HumanDoesNotExistsError` and exception `HumanAlreadyInLoveError` are declared to be raised by 
        the `Human` class method called `love` , but method **missed to raise the second exception**, so the corresponding error should be raised.
        
        ```python
        from accessify import implements, throws
        
        
        class HumanDoesNotExistsError(Exception):
            pass
        
        
        class HumanAlreadyInLoveError(Exception):
            pass
        
        
        class HumanInterface:
        
            @throws(HumanDoesNotExistsError, HumanAlreadyInLoveError)
            def love(self, who, *args, **kwargs):
                pass
        
        
        if __name__ == '__main__':
        
            @implements(HumanInterface)
            class Human:
        
                def love(self, who, *args, **kwargs):
        
                    if who is None:
                        raise HumanDoesNotExistsError('Human whom need to love does not exist.')
        ```
        
        The code above will produce the following traceback.
        
        ```bash
        Traceback (most recent call last):
          File "examples/interfaces/throws.py", line 21, in <module>
            @implements(HumanInterface)
          File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 103, in decorator
            class_method_arguments=class_member.arguments_as_string,
        accessify.errors.DeclaredInterfaceExceptionHasNotBeenImplementedException: Declared exception HumanAlreadyInLoveError by HumanInterface.love() member has not been implemented by Human.love(self, who, args, kwargs)
        ```
        
        Test it out using the [examples](https://github.com/dmytrostriletskyi/accessify/tree/basic-accessibility-levels/examples).
        Get the example that contains the code above by `curl` and run it by `python3`.
        
        ```bash
        $ curl -L https://git.io/fhh26 > throws.py
        $ python3 throws.py
        ```
        
        ## Disable checking
        
        You can disable all `accessify` checks. For instance, in the production, when you shouldn't check it because it already was checked 
        in the development. Use the following environment variable then:
        
        ```bash
        export DISABLE_ACCESSIFY=True
        ```
        
        ## Contributing
        
        Clone the project and install requirements:
        
        ```bash
        $ git clone git@github.com:dmytrostriletskyi/accessify.git && cd accessify
        $ pip3 install -r requirements-dev.txt
        $ pip3 install -r requirements-tests.txt
        ```
        
        If you prefer working with the [Docker](https://www.docker.com) and wanna easily change `Python` environments, follow:
        
        ```bash
        $ git clone git@github.com:dmytrostriletskyi/accessify.git && cd accessify
        $ export ACCESSIFY_PYTHON_VERSION=3.4
        $ docker build --build-arg ACCESSIFY_PYTHON_VERSION=$ACCESSIFY_PYTHON_VERSION -t accessify . -f Dockerfile-python3.x
        $ docker run -v $PWD:/accessify --name accessify accessify
        ```
        
        Enter the container bash, check `Python` version and run tests:
        
        ```bash
        $ docker exec -it accessify bash
        $ root@36a8978cf100:/accessify# python --version
        $ root@36a8978cf100:/accessify# pytest -vv tests
        ```
        
        Clean container and images with the following command:
        
        ```bash
        $ docker rm $(docker ps -a -q) -f
        $ docker rmi $(docker images -q) -f
        ```
        
        When you will make changes, ensure your code pass [the checkers](https://github.com/dmytrostriletskyi/accessify/blob/basic-accessibility-levels/.travis.yml#L15) 
        and is covered by tests using [pytest](https://docs.pytest.org/en/latest).
        
        If you are new for the contribution, please read:
        
        * Read about pull requests — https://help.github.com/en/articles/about-pull-requests
        * Read how to provide pull request — https://help.github.com/en/articles/creating-a-pull-request-from-a-fork
        * Also the useful article about how to contribute — https://akrabat.com/the-beginners-guide-to-contributing-to-a-github-project/
        
        ## References
        
        Check it out to familiarize yourself with class members accessibility levels:
        
        * C# accessibility levels — https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/accessibility-levels
        * Java accessibility levels — https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
        * Object-oriented programming interfaces — https://www.cs.utah.edu/~germain/PPS/Topics/interfaces.html
        * Interfaces in C# — https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface
        * Interfaces in Java — https://docs.oracle.com/javase/tutorial/java/concepts/interface.html
        
Platform: UNKNOWN
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: License :: OSI Approved :: MIT License
Description-Content-Type: text/markdown
