diff --git a/README.rst b/README.rst index 87032d3..118ad3f 100644 --- a/README.rst +++ b/README.rst @@ -2,22 +2,21 @@ positional ========== +A decorator which enforces only some args may be passed positionally. + |PyPi| |Build Status| |Documentation Status| -Intro -===== +The Basics +========== `positional` provides a decorator which enforces only some args may be passed positionally. The idea and some of the code was taken from the oauth2 client of the google-api client. -The Basics -========== - The decorator makes it easy to support Python 3 style key-word only parameters. For example, in Python 3 it is possible to write: @@ -121,18 +120,25 @@ This behaviour will work with the `positional.method` and ... @positional.classmethod() ... def my_method(cls, pos1, kwonly1=None): ... ... - - MyClass.my_method(10) # Ok. - MyClass.my_method(10, 20) # Raises exception. - MyClass.my_method(10, kwonly1=20) # Ok. + ... + >>> MyClass.my_method(10) # Ok. + >>> MyClass.my_method(10, 20) # Raises exception. + >>> MyClass.my_method(10, kwonly1=20) # Ok. For compatibility reasons you may wish to not always raise an exception so a WARN mode is available. Rather than raise an exception a warning message will be logged: - @positional(1, enforcement=positional.WARN): - def fn(pos1, kwonly=1): - ... +.. code:: python + + >>> @positional(1, enforcement=positional.WARN): + ... def fn(pos1, kwonly=1): + ... ... + +Available modes are: + +- positional.EXCEPT - the default, raise an exception. +- positional.WARN - log a warning on mistake. .. |Build Status| image:: https://travis-ci.org/morganfainberg/positional.svg?branch=master diff --git a/doc/source/index.rst b/doc/source/index.rst index 33479bd..281c064 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -6,7 +6,13 @@ Contents: .. toctree:: :maxdepth: 1 - api/positional + api/modules + +Usage +===== + +.. include:: ../../README.rst + Release Notes ============= diff --git a/positional/__init__.py b/positional/__init__.py index ca5c965..02c0b4b 100644 --- a/positional/__init__.py +++ b/positional/__init__.py @@ -21,109 +21,31 @@ _logger = logging.getLogger(__name__) class positional(object): - """A decorator which enforces only some args may be passed positionally. + """A decorator to enforce passing arguments as keywords. - This idea and some of the code was taken from the oauth2 client of the - google-api client. + When you have a function that takes a lot of arguments you expect people to + pass those arguments as keyword arguments. Python however does not enforce + this. In future then if you decide that you want to insert a new argument + or rearrange the arguments or transition to using **kwargs you break + compatibility with users of your code who (wrongly) gave you 20 positional + arguments. - This decorator makes it easy to support Python 3 style key-word only - parameters. For example, in Python 3 it is possible to write:: + In python 3 there is syntax to prevent this however we are not all in the + position where we can write python 3 exclusive code. Positional solves the + problem in the mean time across both pythons by enforcing that certain + arguments must be past as keyword arguments. - def fn(pos1, *, kwonly1, kwonly2=None): - ... + :param max_positional_args: the maixmum number of arguments that can be + passed to this function without keyword parameters. Defaults to + enforcing that every parameter with a default value must be passed as a + keyword argument. + :type max_positional_args int - All named parameters after * must be a keyword:: - - fn(10, 'kw1', 'kw2') # Raises exception. - fn(10, kwonly1='kw1', kwonly2='kw2') # Ok. - - To replicate this behaviour with the positional decorator you simply - specify how many arguments may be passed positionally. To replicate the - example above:: - - @positional(1) - def fn(pos1, kwonly1=None, kwonly2=None): - ... - - If no default value is provided to a keyword argument, it becomes a - required keyword argument:: - - @positional(0) - def fn(required_kw): - ... - - This must be called with the keyword parameter:: - - fn() # Raises exception. - fn(10) # Raises exception. - fn(required_kw=10) # Ok. - - When defining instance or class methods always remember that in python the - first positional argument passed is always the instance so you will need to - account for `self` and `cls`:: - - class MyClass(object): - - @positional(2) - def my_method(self, pos1, kwonly1=None): - ... - - @classmethod - @positional(2) - def my_method(cls, pos1, kwonly1=None): - ... - - If you would prefer not to account for `self` and `cls` you can use the - `method` and `classmethod` helpers which do not consider the initial - positional argument. So the following class is exactly the same as the one - above:: - - class MyClass(object): - - @positional.method(1) - def my_method(self, pos1, kwonly1=None): - ... - - @positional.classmethod(1) - def my_method(cls, pos1, kwonly1=None): - ... - - If a value isn't provided to the decorator then it will enforce that - every variable without a default value will be required to be a kwarg:: - - @positional() - def fn(pos1, kwonly1=None): - ... - - fn(10) # Ok. - fn(10, 20) # Raises exception. - fn(10, kwonly1=20) # Ok. - - This behaviour will work with the `positional.method` and - `positional.classmethod` helper functions as well:: - - class MyClass(object): - - @positional.classmethod() - def my_method(cls, pos1, kwonly1=None): - ... - - MyClass.my_method(10) # Ok. - MyClass.my_method(10, 20) # Raises exception. - MyClass.my_method(10, kwonly1=20) # Ok. - - For compatibility reasons you may wish to not always raise an exception so - a WARN mode is available. Rather than raise an exception a warning message - will be logged:: - - @positional(1, enforcement=positional.WARN): - def fn(pos1, kwonly=1): - ... - - Available modes are: - - - positional.EXCEPT - the default, raise an exception. - - positional.WARN - log a warning on mistake. + :param enforcement: defines the way incorrect usage is reported. Currenlty + accepts :py:attr:`positional.EXCEPT` to raise a TypeError or + :py:attr:`positional.WARN` to print a warning. A warning can be useful + for applying to functions that are already public as a deprecation + notice. Defaults to :py:attr:`positional.EXCEPT`. """ EXCEPT = 'except'