Skip to content

Instantly share code, notes, and snippets.

@omaciel
Last active August 31, 2021 14:54
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save omaciel/8fc0586f313121bded31 to your computer and use it in GitHub Desktop.
Save omaciel/8fc0586f313121bded31 to your computer and use it in GitHub Desktop.
A collection of Python tips and tricks

Flatten a dictionary

  In [1]: k = {'name': 'Og Maciel', 'age': 40, 'mansions': '', 'kids': 3}
  In [2]: ' '.join("--{!s}={!r}".format(key,val) for (key,val) in k.iteritems())
  "--age=40 --kids=3 --name='Og Maciel' --mansions=''"

Run Unittest TestCase from IPython

In [1]: import sys

In [2]: import unittest

In [3]: class MyTest(unittest.TestCase):
   ....:
   ....:     def test_fail(self):
   ....:         self.assertEquals(1, 2)
   ....:     def test_pass(self):
   ....:         self.assertEquals(1, 1)
   ....:

In [4]: suite = unittest.TestLoader().loadTestsFromTestCase(MyTest)

In [5]: unittest.TextTestRunner(verbosity=1,stream=sys.stderr).run(suite)
F.
======================================================================
FAIL: test_fail (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython-input-16-57d405d010ea>", line 4, in test_fail
    self.assertEquals(1, 2)
AssertionError: 1 != 2

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)
Out[5]: <unittest.runner.TextTestResult run=1 errors=0 failures=1>

In [6]: unittest.TextTestRunner(verbosity=1).run(suite)
F.
======================================================================
FAIL: test_fail (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython-input-16-57d405d010ea>", line 4, in test_fail
    self.assertEquals(1, 2)
AssertionError: 1 != 2

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)
Out[6]: <unittest.runner.TextTestResult run=1 errors=0 failures=1>

In [7]: suite = unittest.TestSuite()

In [8]: suite.addTest(MyTest('test_fail'))

In [9]: unittest.TextTestRunner(verbosity=2).run(suite)
test_foo (__main__.MyTest) ... FAIL

======================================================================
FAIL: test_foo (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython-input-2-93e8ecdc485c>", line 4, in test_foo
    self.assertEquals(1, 2)
AssertionError: 1 != 2

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)
Out[9]: <unittest.runner.TextTestResult run=1 errors=0 failures=1>

In [10]: suite = unittest.TestSuite()

In [11]: suite.addTest(MyTest('test_pass'))

In [12]: unittest.TextTestRunner(verbosity=2).run(suite)
test_pass (__main__.MyTest) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Out[12]: <unittest.runner.TextTestResult run=1 errors=0 failures=0>

Playing with Mock

In [1]: import mock

In [2]: class Car(object):
   ...:     def shift_down(self):
   ...:         print "Down"
   ...:     def shift_up(self):
   ...:         print "Up"
   ...:

In [3]: with mock.patch('__main__.Car') as mocked_car:
   ....:     my_car = mocked_car()
   ....:     my_car.shift_up()
   ....:     my_car.shift_down()
   ....:     my_car.foo()
   ....:     print my_car.method_calls
   ....:
[call.shift_up(), call.shift_down(), call.foo()]

In [4]: def beep():
   ...:     return "Beep!"
   ...:

In [5]: with mock.patch.object(Car, 'shift_down') as mocked_shift_down:
   ....:     mocked_shift_down.return_value = beep()
   ....:     my_car = Car()
   ....:     my_car.shift_up()
   ....:     print my_car.shift_down()
   ....:
Up
Beep!

Using Unittest SubTests

>>> import unittest
>>> class MyTest(unittest.TestCase):
...     def test_is_greater_than(self):
...         values = {4, 5, 6}
...         for value in values:
...             with self.subTest(value):
...                 self.assertGreater(value, 1)
...
>>> suite = unittest.TestLoader().loadTestsFromTestCase(MyTest)
>>> unittest.TextTestRunner(verbosity=1).run(suite)
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
<unittest.runner.TextTestResult run=1 errors=0 failures=0>

MetaClass for Data Driven testing

An example of using a python MetaClass for Data Driven testing.

import unittest

from ddt import data
from ddt import ddt


class MetaTest(type):
    """
    Metaclass to tweak test classes dynamically
    """
    def __new__(cls, name, bases, dic):
        """Add test methods to new classes"""

        _klass = super(MetaTest, cls).__new__(cls, name, bases, dic)

        setattr(_klass, "test_greater_than_zero", cls.test_greater_than_zero)

        return _klass

    @staticmethod
    @data(1, 2, 3, 4)
    def test_greater_than_zero(cls, value):
        """Simple test"""
        cls.assertTrue(value > 0)


class Base(unittest.TestCase):
    """Base class for all tests"""

    @classmethod
    def setUpClass(cls):
        """Adds a baz attribute to test classes"""
        cls.baz = "Bazinga"


@ddt
class TestMetaClass(Base):
    """A simple test class"""

    __metaclass__ = MetaTest

    def test_baz_exists(self):
        """Checks that baz is not None"""
        self.assertTrue(self.baz)

This will give you:

$ nosetests test_metaclass.py
test_baz_exists (tests.cli.test_foo.TestMetaClass) ... ok
test_greater_than_zero_1 (tests.cli.test_foo.TestMetaClass) ... ok
test_greater_than_zero_2 (tests.cli.test_foo.TestMetaClass) ... ok
test_greater_than_zero_3 (tests.cli.test_foo.TestMetaClass) ... ok
test_greater_than_zero_4 (tests.cli.test_foo.TestMetaClass) ... ok
Ran 5 tests in 0.001s
OK

PhantomJS

Using PhantomJS for quick UI testing using Selenium's WebDriver.

In [1]: from selenium import webdriver

In [2]: service_args = ['--ignore-ssl-errors=true']

In [3]: driver = webdriver.PhantomJS(service_args=service_args)

In [4]: driver.get("https://aaa.bbb.ccc")

In [5]: driver.title
Out[5]: u'Login'

Import On Demand

def render(engine_id, template, **kwargs):
  engines = {
    "jinja2": "jinja2",
    "mako": "make.template",
    }
    try:
      module = engines[engine_id]
      engine = __import__(
        module, globals(), locals(), ['Template'], -1)
    except (KeyError, ImportError) as err:
      print "Template engine not found!"
      return ''
    return engine.Template(template).render(**kwargs)

print render("jinja2", "Hi {{f}}", f="John")
print render("mako", "Hi ${f} ${l}", f="John", l="Doe")

See if a module exists without actually importing it

from importlib.util import find_spec
if find_spec('modname'):
    print('yep')

Get Logging for Python console

import logging
logging.getLogger().setLevel(logging.DEBUG)

Python Decorators

Without arguments

Usage equivalent to f = decorator_name(f)

import functools

def decorator_name(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):

        # do work before original function

        result = f(*args, **kwargs)

        # do work after original function
        # modify result if necessary

        return result
    return wrapped

With arguments

Usage equivalent to f = decorator_name(args)(f)

import functools

def decorator_name(args):
    def decorator(f):
        @functools.wraps(f)
        def wrapped(*args, **kwargs):

            # do work before original function

            result = f(*args, **kwargs)

            # do work after original function
            # modify result if necessary

            return result
        return wrapped
    return decorator

Wraps

from functools import wraps


def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x

print f.__name__  # prints 'f'
print f.__doc__   # prints 'does some math'

Tokenizer

  • File: tokenize-example-1.py
import tokenize

file = open("tokenize-example-1.py")

def handle_token(type, token, (srow, scol), (erow, ecol), line):
    print "%d,%d-%d,%d:\t%s\t%s" % \
        (srow, scol, erow, ecol, tokenize.tok_name[type], repr(token))

tokenize.tokenize(
    file.readline,
    handle_token
    )

Outputs:

1,0-1,6:     NAME    'import'
1,7-1,15:    NAME    'tokenize'
1,15-1,16:   NEWLINE '\012'
2,0-2,1:     NL      '\012'
3,0-3,4:     NAME    'file'
3,5-3,6:     OP      '='
3,7-3,11:    NAME    'open'
3,11-3,12:   OP      '('
3,12-3,35:   STRING  '"tokenize-example-1.py"'
3,35-3,36:   OP      ')'
3,36-3,37:   NEWLINE '\012'
...
  • The tokenize function takes two callable objects; the first argument is called repeatedly to fetch new code lines, and the second argument is called for each token.
  • The generator produces 5-tuples with these members:
    • the token type;
    • the token string;
    • a 2-tuple (srow, scol) of ints specifying the row and column where the token begins in the source;
    • a 2-tuple (erow, ecol) of ints specifying the row and column where the token ends in the source;
    • and the line on which the token was found. The line passed (the last tuple item) is the logical line; continuation lines are included.

Switch Statement

def dispatch_dic(operator, x, y):
    return {
        'add': lambda: x + y,
        'sub': lambda: x - y,
        'mul': lambda: x * y,
        'div': lambda: x / y,
    }.get(operator, lambda: None)()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment