david alfonso

Watson: Codebase review

Project information

Python version support

At first, I had installed the Debian 10 packages (python3-watson and watson), but I wanted to review and use the latest version, preferably in a virtualenv, so I decided to get the latest code from the GitHub repository.

Just by looking at the Debian package names you can notice that Watson supports Python 3, but does it also support Python 2?

Let's take a look at the tox.ini file which is used for running tests in multiple python environments:

envlist = flake8,py27,py34,py35,py36,py37

We can see that Watson effectively supports python 2.7 which is pretty typical these days.



Being packaged in Debian, this means we can just look up for its runtime dependencies in a Debian system:

$ apt-cache depends watson python3-watson | awk '/Depends: [^<]/ {print $2}' | xargs dpkg -l | grep ii
ii  python3-arrow    0.12.1-2     all   Python3 library to manipulate dates, times, and timestamps
ii  python3-click    7.0-1        all   Wrapper around optparse for command line utilities - Python 3.x
ii  python3-requests 2.21.0-1     all   elegant and simple HTTP library for Python3, built for human beings
ii  python3-watson   1.6.0-6      all   Library for Watson (Python 3)


Besides this high-level info, there are two requirements files in the root folder, but why? Let's grep around and see if we can get more info:

# some results are not shown as seemed irrelevant to me
$ grep -r requirements *
Makefile:   $(PIP) install -r requirements-dev.txt
MANIFEST.in:include requirements-dev.txt
MANIFEST.in:include requirements.txt
setup.py:    install_requires=parse_requirements('requirements.txt'),
setup.py:    tests_require=parse_requirements('requirements-dev.txt'),
docs/contributing/hack.md:        $ pip install -r requirements-dev.txt

As for the requirements-dev.txt, we can see tools for running the tests, generating the documentation, and more:

Folder organization

The file structure is as follows:

Code organization

$ cloc --by-file watson
File                              blank        comment           code
watson/cli.py                       237            445            848
watson/watson.py                    110             50            395
watson/fullmoon.py                    7              6            220
watson/utils.py                      53             63            168
watson/frames.py                     38              0            117
watson/config.py                     33             47             32
watson/__main__.py                    1              0              5
watson/__init__.py                    1              0              3
watson/version.py                     1              1              1
SUM:                                481            612           1789

$ cloc --by-file tests
File                                  blank        comment           code
tests/test_watson.py                    251             44            570
tests/test_utils.py                      56              4            170
tests/test_config.py                     24             47             82
tests/__init__.py                        15              1             28
tests/test_fullmoon.py                    6              1             19
tests/conftest.py                         6              1              8
SUM:                                    358             98            877


You can run all tests in all available Python environments in your system like this:

$ tox

Watson uses pytest along with unittest.mock to do unit testing.

Let's understand some testing constructs used in Watson:

# In tests/__init__.py
# path.object(target, attribute, new) with new being the new object to replace the target
return mock.patch.object(dt_module, 'datetime', MockedDateTime)
$ pytest --fixtures
--------------------------------------- fixtures defined from pytest_datafiles ----------------------------------------
    pytest fixture to define a 'tmpdir' containing files or directories
    specified with a 'datafiles' mark.

------------------------------------------ fixtures defined from pytest_mock ------------------------------------------
    return an object that has the same interface to the `mock` module, but
    takes care of automatically undoing all patches after each test method.
    Same as "mocker", but kept only for backward compatibility.

---------------------------------------- fixtures defined from tests.conftest -----------------------------------------
    tests/conftest.py:14: no docstring available
    tests/conftest.py:9: no docstring available

--------------------------------------- fixtures defined from tests.test_watson ---------------------------------------
    tests/test_watson.py:31: no docstring available