Skip to content

Commit

Permalink
Merge pull request #16 from ddmee/readthedocs
Browse files Browse the repository at this point in the history
Readthedocs for polling2
  • Loading branch information
ddmee authored Sep 11, 2020
2 parents ffaf1fc + dd5fddb commit e136a05
Show file tree
Hide file tree
Showing 16 changed files with 645 additions and 230 deletions.
24 changes: 24 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py

# Build documentation with MkDocs
#mkdocs:
# configuration: mkdocs.yml

# Optionally build your docs in additional formats such as PDF
# formats:
# - pdf

# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.7
install:
- requirements: docs/requirements.txt
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Justin Iso, 2019 Donal Mee
Copyright (c) 2015 Justin Iso, 2019-2020 Donal Mee

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
198 changes: 11 additions & 187 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
[![Build Status](https://travis-ci.org/ddmee/polling2.svg?branch=master)](https://travis-ci.org/ddmee/polling2)
[![Build Status](https://travis-ci.com/ddmee/polling2.svg?branch=master)](https://travis-ci.org/ddmee/polling2)
[![PyPI](https://img.shields.io/pypi/dm/polling2.svg)]()
[![PyPI](https://img.shields.io/pypi/v/polling2.svg)]()
[![HitCount](http://hits.dwyl.io/ddmee/polling2.svg)](http://hits.dwyl.io/ddmee/polling2)

polling2
=============
# polling2

_Never write another polling function again!_

Documentation available at [Read the Docs](https://polling2.readthedocs.io)

Polling2 is a powerful python utility used to wait for a function to return a certain expected condition.

Some possible uses cases include:
Expand All @@ -20,15 +21,15 @@ Polling2 is handy for getting rid of all that duplicated polling-code. Often, ap

Polling2 is a fork of the original [polling](https://github.com/justiniso/polling). It was forked when the original maintainer failed to respond to issues or PRs.

Polling2 is ++under active development++. Would you like to see a particular feature? Ask and thou shall recieve.
Polling2 is _under active development_. Would you like to see a particular feature? Ask and thou shall recieve.

# Installation
## Installation

```
pip install polling2
```

# Development installation
## Development installation

```shell
# install lib, but use system links from the repo into sitepackages.
Expand All @@ -39,30 +40,7 @@ python setup.py test
pytest tests
```

# Examples

### Example: Poll every minute until a url returns 200 status code

```python
import requests
polling2.poll(
lambda: requests.get('http://google.com').status_code == 200,
step=60,
poll_forever=True)
```

If you are creating a new cloud provider instance (e.g. waiting for an EC2 instance to come online), you can continue to poll despite getting ConnectionErrors:

```python
import requests
polling2.poll(
lambda: requests.get('your.instance.ip').status_code == 200,
step=60,
ignore_exceptions=(requests.exceptions.ConnectionError,),
poll_forever=True)
```

### Example: Poll for a file to exist
## Example:

```python
# This call will wait until the file exists, checking every 0.1 seconds and stopping after 3 seconds have elapsed
Expand All @@ -75,163 +53,9 @@ file_handle = polling2.poll(
# Polling will return the value of your polling function, so you can now interact with it
file_handle.close()
```

### Example: Polling for Selenium WebDriver elements

```python
from selenium import webdriver
driver = webdriver.Firefox()

driver.get('http://google.com')
search_box = polling2.poll(
lambda: driver.find_element_by_id('search'),
step=0.5,
timeout=7)

search_box.send_keys('python polling')
```

### Example: Using the polling timeout exception

```python
# An exception will be raised by the polling function on timeout (or the maximum number of calls is exceeded).
# This exception will have a 'values' attribute. This is a queue with all values that did not meet the condition.
# You can access them in the except block.

import random
try:
polling2.poll(lambda: random.choice([0, (), False]), step=0.5, timeout=1)
except polling2.TimeoutException, te:
while not te.values.empty():
# Print all of the values that did not meet the exception
print te.values.get()
```

### Example: Using a custom checker

```python
# is_truthy() is the default checker for the parameter check_success. But, it's easy to create a custom
# checker function, that tests whether the value returned by the target is the expected value.

# Here the target is going to return None, which the custom checker, created by is_value(None)
# will return True for.
polling2.poll(target=lambda: None, step=0.1, max_tries=1, check_success=polling2.is_value(None))
# Or another example, where we can test that False is returned by the target.
polling2.poll(target=lambda: False, step=0.1, max_tries=1, check_success=polling2.is_value(False))
```

### Example: Using a custom condition callback function

```python
import requests

def is_correct_response(response):
"""Check that the response returned 'success'"""
return response == 'success'

polling2.poll(
lambda: requests.put('http://mysite.com/api/user', data={'username': 'Jill'},
check_success=is_correct_response,
step=1,
timeout=10)
```

### Example: Logging the return values from the target function.

```python
import logging
import requests

def is_correct_response(response):
"""Check that the response returned 'success'"""
return response == 'success'

polling2.poll(
lambda: requests.put('http://mysite.com/api/user', data={'username': 'Jill'},
check_success=is_correct_response,
step=1,
timeout=10,
log=logging.DEBUG)
```

This will log the string representation of response object to python's logging module at the debug level.
A message like this will be sent to the log for each return value. You can change the level by providing
a different value to the log parameter.

```text
poll() calls check_success(<Response [200]>)
```

There is also an option to log the exceptions that are caught by ignore_exceptions. Note, the full-exception traceback
will not be printed in the logs. Instead, the error and it's message (using %r formatting) will appear. In the following
code snippet, the ValueError raised by the function `raises_error()` will be sent to the logger at the 'warning' level.

```python
import polling2
import logging
import mock

# basicConfig should sent warning level messages to the stdout.
logging.basicConfig()

# Create a function that raises a ValueError, then a RuntimeError.
raises_error = mock.Mock(side_effect=[ValueError('a message'), RuntimeError])

try:
polling2.poll(
target=raises_error,
step=0.1,
max_tries=3,
ignore_exceptions=(ValueError), # Only ignore the ValueError.
log_error=logging.WARNING # Ignored errors should be passed to the logger at warning level.
)
except RuntimeError as _e:
print "Un-ignored %r" % _e``
```

# Future extensions

- Add poll_killer(). Specify a hard timeout so that if the function being polled blocks and doesn't return, poll_killer() will raise a timeout.
- Add an option to do via multiprocessing.
- Add an option to do via threading - probably the default option.
- Add poll_chain(). Have reason to poll a bunch of functions in a row? poll_chain() allows you to chain a bunch of polling functions together.
- Allow step to be specificed as 0, so that we can poll continously. (Perhaps it's best to write a poll_continous() method.)

# Release notes

## 0.4.5
- 'Begin poll(*)' message is logged when poll() is called. Hopefully this means the user doesn't feel the need to write a message before
every call to poll() to indicate how long the poll() might take.

## 0.4.4
- Add is_value() function. A function that allows a user to easily build a custom checker, like is_truthy(), but for any value.

## 0.4.3
- Add log_error parameter to the poll signature. Enables logging of ignored exceptions.

## 0.4.2
- Add log_value() decorator and log parameter to poll signature. Enables logging of return_values.

## 0.4.0
- Fixed polling function from waiting another sleep whenever the max_tries value has reached zero.
- Remove test-only dependencies from requirements to install the package.
- No longer testing on python 2.6. Add support for travis testing on python 3.6 and pypy 3.5.
- Creation of polling2, forked from polling as previous maintainer seems to be ignoring issues and pull-requests.
- Remove ```*a, **k``` from poll signature. This allows Type errors to be raised if caller spells arguments into correctly, making bugs easier to find.

## 0.3.0

- Support Python 3.4+

## 0.2.0

- Allow users to access a "last" attribute on the exceptions. This should hold the last evaluated value, which is the more common use case than getting the first value.
- Fix a bug that actually ran 1 more time than value specified by max_tries

## 0.1.0
There are [more examples](https://polling2.readthedocs.io/en/latest/examples) in the documentation.

- First version
## API and user guide at [Read the Docs](https://polling2.readthedocs.io)

# Contributors
- Justin Iso (original creator)
- Donal Mee
[![Read the Docs](https://raw.githubusercontent.com/ddmee/polling2/master/ext/read_the_docs.png)](https://polling2.readthedocs.io)
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
35 changes: 35 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
The API
=======

.. module:: polling2

This part of the documentation covers all the interfaces of Requests. For
parts where Requests depends on external libraries, we document the most
important right here and provide links to the canonical documentation.


Poll Method
-----------

The main method is the poll method.

.. autofunction:: poll


Helper Methods
--------------

There are some other supplemental methods that can help out.

.. autofunction:: step_constant
.. autofunction:: step_linear_double
.. autofunction:: is_truthy
.. autofunction:: is_value
.. autofunction:: log_value

Exceptions
----------

.. autoexception:: PollingException
.. autoexception:: TimeoutException
.. autoexception:: MaxCallException
72 changes: 72 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
import sys
# sys.path.insert(0, os.path.abspath('.'))
from pathlib import Path
sys.path.append(str(Path('../').resolve()))
import polling2

# -- Project information -----------------------------------------------------

project = 'Polling2'
copyright = '2020, Donal Mee'
author = 'Donal Mee'

# The full version, including alpha/beta/rc tags
release = polling2.__version__
version = release


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.viewcode",
]


# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'

html_theme_options = {
"show_powered_by": False,
"github_user": "ddmee",
"github_repo": "polling2",
"github_banner": True,
"show_related": False
}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
html_show_copyright = True
Loading

0 comments on commit e136a05

Please sign in to comment.