requirements_dev.txt

mypy
epc
importmagic
'python-language-server[all]'
'ptvsd>=4.2'
black
pytest==5.0.1

starlette config

pretty nifty snippet for configs in python projects

import os
import typing
from collections.abc import MutableMapping


class undefined:
    pass


class EnvironError(Exception):
    pass


class Environ(MutableMapping):
    def __init__(self, environ: typing.MutableMapping = os.environ):
        self._environ = environ
        self._has_been_read = set()  # type: typing.Set[typing.Any]

    def __getitem__(self, key: typing.Any) -> typing.Any:
        self._has_been_read.add(key)
        return self._environ.__getitem__(key)

    def __setitem__(self, key: typing.Any, value: typing.Any) -> None:
        if key in self._has_been_read:
            raise EnvironError(
                f"Attempting to set environ['{key}'], but the value has already be read."
            )
        self._environ.__setitem__(key, value)

    def __delitem__(self, key: typing.Any) -> None:
        if key in self._has_been_read:
            raise EnvironError(
                f"Attempting to delete environ['{key}'], but the value has already be read."
            )
        self._environ.__delitem__(key)

    def __iter__(self) -> typing.Iterator:
        return iter(self._environ)

    def __len__(self) -> int:
        return len(self._environ)


environ = Environ()


class Config:
    def __init__(
        self, env_file: str = None, environ: typing.Mapping[str, str] = environ
    ) -> None:
        self.environ = environ
        self.file_values = {}  # type: typing.Dict[str, str]
        if env_file is not None and os.path.isfile(env_file):
            self.file_values = self._read_file(env_file)

    def __call__(
        self, key: str, cast: type = None, default: typing.Any = undefined
    ) -> typing.Any:
        return self.get(key, cast, default)

    def get(
        self, key: str, cast: type = None, default: typing.Any = undefined
    ) -> typing.Any:
        if key in self.environ:
            value = self.environ[key]
            return self._perform_cast(key, value, cast)
        if key in self.file_values:
            value = self.file_values[key]
            return self._perform_cast(key, value, cast)
        if default is not undefined:
            return self._perform_cast(key, default, cast)
        raise KeyError(f"Config '{key}' is missing, and has no default.")

    def _read_file(self, file_name: str) -> typing.Dict[str, str]:
        file_values = {}  # type: typing.Dict[str, str]
        with open(file_name) as input_file:
            for line in input_file.readlines():
                line = line.strip()
                if "=" in line and not line.startswith("#"):
                    key, value = line.split("=", 1)
                    key = key.strip()
                    value = value.strip().strip("\"'")
                    file_values[key] = value
        return file_values

    def _perform_cast(
        self, key: str, value: typing.Any, cast: type = None
    ) -> typing.Any:
        if cast is None or value is None:
            return value
        elif cast is bool and isinstance(value, str):
            mapping = {"true": True, "1": True, "false": False, "0": False}
            value = value.lower()
            if value not in mapping:
                raise ValueError(
                    f"Config '{key}' has value '{value}'. Not a valid bool."
                )
            return mapping[value]
        try:
            return cast(value)
        except (TypeError, ValueError):
            raise ValueError(
                f"Config '{key}' has value '{value}'. Not a valid {cast.__name__}."
            )

example from starlette docs

app.py:

import databases

from starlette.applications import Starlette
from starlette.config import Config
from starlette.datastructures import CommaSeparatedStrings, Secret

# Config will be read from environment variables and/or ".env" files.
config = Config(".env")

DEBUG = config('DEBUG', cast=bool, default=False)
DATABASE_URL = config('DATABASE_URL', cast=databases.DatabaseURL)
SECRET_KEY = config('SECRET_KEY', cast=Secret)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=CommaSeparatedStrings)

app = Starlette()
app.debug = DEBUG

.env:

# Don't commit this to source control.
# Eg. Include ".env" in your `.gitignore` file.
DEBUG=True
DATABASE_URL=postgresql://localhost/myproject
SECRET_KEY=43n080musdfjt54t-09sdgr
ALLOWED_HOSTS=127.0.0.1, localhost

count the digits of a positive integer without casting to a string

def count_digits(number, acc=9, iterations=1):
    if number - acc <= 0:
        return iterations
    return count_digits(number, 10**iterations*9 + acc, iterations+1)


print(count_digits(9))
print(count_digits(10))
print(count_digits(99999))

Click

Tool for making cli apps with python

examples

import sys
import click

@click.group()
def cli():
    pass

@cli.command()
def initdb():
    click.echo('Initialized the database')

@cli.command()
def dropdb():
    click.echo('Dropped the database')

if __name__ == '__main__':
    sys.exit(cli())

###############################################################

@click.command()
@click.option('--count', default=1, help='number of greetings')
@click.argument('name')
def hello(count, name):
    for x in range(count):
        click.echo('Hello %s!' % name)

# $ python hello.py --help
# Usage: hello.py [OPTIONS] NAME

# Options:
#   --count INTEGER  number of greetings
#   --help           Show this message and exit.

cookiecutter

cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git
Note: cookiecutter link is not officially a part of Click, but it's a nice default template for getting a click project started

Django

Batteries included web framework

vscode configs

pipenv vscode

editing for a project src

find virtualenv path

pipenv --py

local settings file that VS Code will use for your project

mkdir .vscode && touch .vscode/settings.json

set the path to your virtualenv in `.vscode/settings.json`

{
    "files.exclude": {
        "**/.git": true,
        "**/.svn": true,
        "**/.hg": true,
        "**/CVS": true,
        "**/.DS_Store": true,
        "**/*.pyc": true,
        "**/__pycache__": true
    },
    "python.pythonPath": "<VIRTUALENV_PYTHON_PATH_HERE>"
}

add pytest args for project

add the follwin lines to your .vscode/settings.json

 "python.testing.pyTestArgs": [
    "--ds=api.config.settings.test",
    "--disable-warnings"
  ],

black format to 80 lines

"python.formatting.provider": "black",
"python.formatting.blackArgs": ["--line-length", "80"],

.gitignore

# Byte-compiled / optimized / DLL files
__pycache__/
,*.py[cod]
,*$py.class

# C extensions
,*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
,*.egg-info/
.installed.cfg
,*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
,*.manifest
,*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
,*.cover
.hypothesis/
.pytest_cache/

# Translations
,*.mo
,*.pot

# Django stuff:
,*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/
# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

  # pipenv
  #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
  #   However, in case of collaboration, if having platform-specific dependencies or dependencies
  #   having no cross-platform support, pipenv may install dependencies that don’t work, or not
  #   install all needed dependencies.
  #Pipfile.lock

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
,*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
#  vscode
.vscode
.dirs-locals.el

flake8

inline ignore errors

example = lambda: 'example'  # noqa: E731

python style guide example

sourcefile

# -*- coding: utf-8 -*-
"""Example Google style docstrings.

This module demonstrates documentation as specified by the `Google Python
Style Guide`_. Docstrings may extend over multiple lines. Sections are created
with a section header and a colon followed by a block of indented text.

Example:
    Examples can be given using either the ``Example`` or ``Examples``
    sections. Sections support any reStructuredText formatting, including
    literal blocks::

        $ python example_google.py

Section breaks are created by resuming unindented text. Section breaks
are also implicitly created anytime a new section starts.

Attributes:
    module_level_variable1 (int): Module level variables may be documented in
        either the ``Attributes`` section of the module docstring, or in an
        inline docstring immediately following the variable.

        Either form is acceptable, but the two should not be mixed. Choose
        one convention to document module level variables and be consistent
        with it.

Todo:
    * For module TODOs
    * You have to also use ``sphinx.ext.todo`` extension

.. _Google Python Style Guide:
   http://google.github.io/styleguide/pyguide.html

"""

module_level_variable1 = 12345

module_level_variable2 = 98765
"""int: Module level variable documented inline.

The docstring may span multiple lines. The type may optionally be specified
on the first line, separated by a colon.
"""


def function_with_types_in_docstring(param1, param2):
    """Example function with types documented in the docstring.

    `PEP 484`_ type annotations are supported. If attribute, parameter, and
    return types are annotated according to `PEP 484`_, they do not need to be
    included in the docstring:

    Args:
        param1 (int): The first parameter.
        param2 (str): The second parameter.

    Returns:
        bool: The return value. True for success, False otherwise.

    .. _PEP 484:
        https://www.python.org/dev/peps/pep-0484/

    """


def function_with_pep484_type_annotations(param1: int, param2: str) -> bool:
    """Example function with PEP 484 type annotations.

    Args:
        param1: The first parameter.
        param2: The second parameter.

    Returns:
        The return value. True for success, False otherwise.

    """


def module_level_function(param1, param2=None, *args, **kwargs):
    """This is an example of a module level function.

    Function parameters should be documented in the ``Args`` section. The name
    of each parameter is required. The type and description of each parameter
    is optional, but should be included if not obvious.

    If \*args or \*\*kwargs are accepted,
    they should be listed as ``*args`` and ``**kwargs``.

    The format for a parameter is::

        name (type): description
            The description may span multiple lines. Following
            lines should be indented. The "(type)" is optional.

            Multiple paragraphs are supported in parameter
            descriptions.

    Args:
        param1 (int): The first parameter.
        param2 (:obj:`str`, optional): The second parameter. Defaults to None.
            Second line of description should be indented.
        *args: Variable length argument list.
        **kwargs: Arbitrary keyword arguments.

    Returns:
        bool: True if successful, False otherwise.

        The return type is optional and may be specified at the beginning of
        the ``Returns`` section followed by a colon.

        The ``Returns`` section may span multiple lines and paragraphs.
        Following lines should be indented to match the first line.

        The ``Returns`` section supports any reStructuredText formatting,
        including literal blocks::

            {
                'param1': param1,
                'param2': param2
            }

    Raises:
        AttributeError: The ``Raises`` section is a list of all exceptions
            that are relevant to the interface.
        ValueError: If `param2` is equal to `param1`.

    """
    if param1 == param2:
        raise ValueError('param1 may not be equal to param2')
    return True


def example_generator(n):
    """Generators have a ``Yields`` section instead of a ``Returns`` section.

    Args:
        n (int): The upper limit of the range to generate, from 0 to `n` - 1.

    Yields:
        int: The next number in the range of 0 to `n` - 1.

    Examples:
        Examples should be written in doctest format, and should illustrate how
        to use the function.

        >>> print([i for i in example_generator(4)])
        [0, 1, 2, 3]

    """
    for i in range(n):
        yield i


class ExampleError(Exception):
    """Exceptions are documented in the same way as classes.

    The __init__ method may be documented in either the class level
    docstring, or as a docstring on the __init__ method itself.

    Either form is acceptable, but the two should not be mixed. Choose one
    convention to document the __init__ method and be consistent with it.

    Note:
        Do not include the `self` parameter in the ``Args`` section.

    Args:
        msg (str): Human readable string describing the exception.
        code (:obj:`int`, optional): Error code.

    Attributes:
        msg (str): Human readable string describing the exception.
        code (int): Exception error code.

    """

    def __init__(self, msg, code):
        self.msg = msg
        self.code = code


class ExampleClass(object):
    """The summary line for a class docstring should fit on one line.

    If the class has public attributes, they may be documented here
    in an ``Attributes`` section and follow the same formatting as a
    function's ``Args`` section. Alternatively, attributes may be documented
    inline with the attribute's declaration (see __init__ method below).

    Properties created with the ``@property`` decorator should be documented
    in the property's getter method.

    Attributes:
        attr1 (str): Description of `attr1`.
        attr2 (:obj:`int`, optional): Description of `attr2`.

    """

    def __init__(self, param1, param2, param3):
        """Example of docstring on the __init__ method.

        The __init__ method may be documented in either the class level
        docstring, or as a docstring on the __init__ method itself.

        Either form is acceptable, but the two should not be mixed. Choose one
        convention to document the __init__ method and be consistent with it.

        Note:
            Do not include the `self` parameter in the ``Args`` section.

        Args:
            param1 (str): Description of `param1`.
            param2 (:obj:`int`, optional): Description of `param2`. Multiple
                lines are supported.
            param3 (:obj:`list` of :obj:`str`): Description of `param3`.

        """
        self.attr1 = param1
        self.attr2 = param2
        self.attr3 = param3  #: Doc comment *inline* with attribute

        #: list of str: Doc comment *before* attribute, with type specified
        self.attr4 = ['attr4']

        self.attr5 = None
        """str: Docstring *after* attribute, with type specified."""

    @property
    def readonly_property(self):
        """str: Properties should be documented in their getter method."""
        return 'readonly_property'

    @property
    def readwrite_property(self):
        """:obj:`list` of :obj:`str`: Properties with both a getter and setter
        should only be documented in their getter method.

        If the setter method contains notable behavior, it should be
        mentioned here.
        """
        return ['readwrite_property']

    @readwrite_property.setter
    def readwrite_property(self, value):
        value

    def example_method(self, param1, param2):
        """Class methods are similar to regular functions.

        Note:
            Do not include the `self` parameter in the ``Args`` section.

        Args:
            param1: The first parameter.
            param2: The second parameter.

        Returns:
            True if successful, False otherwise.

        """
        return True

    def __special__(self):
        """By default special members with docstrings are not included.

        Special members are any methods or attributes that start with and
        end with a double underscore. Any special member with a docstring
        will be included in the output, if
        ``napoleon_include_special_with_doc`` is set to True.

        This behavior can be enabled by changing the following setting in
        Sphinx's conf.py::

            napoleon_include_special_with_doc = True

        """
        pass

    def __special_without_docstring__(self):
        pass

    def _private(self):
        """By default private members are not included.

        Private members are any methods or attributes that start with an
        underscore and are *not* special. By default they are not included
        in the output.

        This behavior can be changed such that private members *are* included
        by changing the following setting in Sphinx's conf.py::

            napoleon_include_private_with_doc = True

        """
        pass

    def _private_without_docstring(self):
        pass

access arrays with a boolean python

def foo(x):
    return ["cat","dog"][x]

foo(True)
#  >> 'dog'
foo(False)
#  >> 'cat'

make things secret src

class Secret:
    """
    Holds a string value that should not be revealed in tracebacks etc.
    You should cast the value to `str` at the point it is required.
    """

    def __init__(self, value: str):
        self._value = value

    def __repr__(self) -> str:
        class_name = self.__class__.__name__
        return f"{class_name}('**********')"

    def __str__(self) -> str:
        return self._value

asyncio src

from aiohttp import ClientSession
import asyncio
from time import time


async def async_request(url: str):
    async with ClientSession() as s:
        resp = await s.get(url)
        result = len(await resp.read())
    print(url)
    return f'{url} - {result}'


async def sync_sample():
    print('run synchronously')
    res1 = await async_request('https://google.com')
    res2 = await async_request('https://amazon.com')
    res3 = await async_request('https://peerfit.com')
    print(res1, res2, res3)


async def create_task_sample():
    print('run concurrently using `asyncio.create_task`')
    task1 = asyncio.create_task(async_request('https://google.com'))
    task2 = asyncio.create_task(async_request('https://amazon.com'))
    task3 = asyncio.create_task(async_request('https://peerfit.com'))

    res1 = await task1
    res2 = await task2
    res3 = await task3

    print(res1, res2, res3)


async def gather_sample():
    print('run concurrently using `asyncio.gather`')
    res1, res2, res3 = await asyncio.gather(
        async_request('https://google.com'),
        async_request('https://amazon.com'),
        async_request('https://peerfit.com'),
    )

    print(res1, res2, res3)


async def runner():
    t1 = time()
    # await sync_sample()
    await gather_sample()
    # await create_task_sample()
    print(time() - t1)

loop = asyncio.get_event_loop()
loop.run_until_complete(runner())

Check if key exists in dictionary

d ={}
key = 'foo'
value = 'bar'

if key not in d:
    d[key] = value
    return d[key]
#>> 'bar'

: bar

logging

basic

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)

absolute path to file

import os
SCRIPT_DIR = os.path.dirname(__file__)  # <-- absolute dir the script is in
REL_PATH = "angel-list-batch-apply.js"
ANGEL_SCRIPT_FILE_PATH = os.path.join(SCRIPT_DIR, REL_PATH)

all words from file

def get_words_from_file(filename: str) -> list:
    """
    Returns list of words from a file
    """
    with open(filename) as f:
        return [word for line in f for word in line.split()]

decorators

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

def say_whee():
    print("Whee!")

say_whee = my_decorator(say_whee)

f strings

variables

foo = "bar"
print(f"hello {foo}")
# hello bar

: hello bar

multiline 1

name = "Eric"
profession = "comedian"
affiliation = "Monty Python"
message = (
    f"Hi {name}. "
    f"You are a {profession}. "
    f"You were in {affiliation}"
)
print(message)
# Hi Eric. You are a comedian. You were in Monty Python

: Hi Eric. You are a comedian. You were in Monty Python remember that you need to place an f in front of each line of a multiline string. The following code won’t work:

name = "Eric"
profession = "comedian"
affiliation = "Monty Python"
message = (
    f"Hi {name}. "
     "You are a {profession}. "
     "You were in {affiliation}"
)
print(message)
 # Hi Eric. You are a {profession}. You were in {affiliation}

: Hi Eric. You are a {profession}. You were in {affiliation}

current directory2

file

To get the full path to the directory a Python file is contained in, write this in that file:

import os
dir_path = os.path.dirname(os.path.realpath(__file__))

current working directory

import os
cwd = os.getcwd()

Personal PyPI

If you want to install packages from a source other than PyPI (say, if your packages are proprietary), you can do it by hosting a simple HTTP server, running from the directory which holds those packages which need to be installed.

Showing an example is always beneficial

For example, if you want to install a package called MyPackage.tar.gz, and assuming this is your directory structure:

ls archive
MyPackage
MyPackage.tar.gz

Go to your command prompt and type:

$ cd archive
$ python -m SimpleHTTPServer 9000

This runs a simple HTTP server running on port 9000 and will list all packages (like MyPackage). Now you can install MyPackage using any Python package installer. Using pip, you would do it like:

$ pip install --extra-index-url=http://127.0.0.1:9000/ MyPackage

design patterns src slides

Singleton

class Tigger:
    """
    The wonderful thing about Tigger's is that they are singletons
    """
    def __str__(self):
        return "I'm the only one!"

    def roar(self):
        return 'Grrr!'

# make this a singleton

# modules with underscores are not imported automatically
class _Tigger:
    """
    The wonderful thing about Tigger's is that they are singletons
    """
    def __str__(self):
        return "I'm the only one!"

    def roar(self):
        return 'Grrr!'

_instance = None

def get_tigger():
    global _instance
    if _instance is None:
        _instance = _Tigger()
    return _instance

a = get_tigger()
b = get_tigger()

id(a) == id(b)

Template Method

behaviorial design pattern that defines the skeleton of an algorithm in the base class but lets a derived class overide specific steps of the algorithm

from abc import ABC, abstractmethod


# ABC is AbstractBaseClass
class AverageCalculator(ABC):

    def average(self):
        try:
            num_items = 0
            total_sum = 0
            while self.has_next():
                total_sum += self.next_item()
                num_items += 1
            if num_items == 0:
                raise RuntimeError("Can't compute the average of zero items.")
            return total_sum / num_items
        finally:
            self.dispose()

    # abstractmethod will error if the class the inherits this doesn't implement it
    @abstractmethod
    def has_next(self):
        pass

    @abstractmethod
    def next_item(self):
        pass

    def dispose(self):
        pass

python brainteaser

def x():
    try:
        return 1
    except:
        return 2
    else:
        return 3
    finally:
        return 4

print(x())

: 4

string between

def string_between(first: str, second: str, string: str):
    """
    https://stackoverflow.com/a/16835195
    """
    return string[string.find(first) + 1 : string.find(second)]

Footnotes

https://stackoverflow.com/questions/5137497/find-current-directory-and-files-directory

https://realpython.com/python-f-strings/