Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul many things #11

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Tests

on:
workflow_dispatch:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
- cron: "0 0 * * 0" # weekly

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
# Pin to an exact 3.9 version due to https://github.com/fudge-py/fudge/issues/13
python-version: [3.6, 3.7, 3.8, "3.9.6"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies and this library
run: pip install -e .[test]
- name: Run tests
run: nosetests
lint:
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.9.6" # Latest python that still works with fudge
- name: Install dependencies and this library
run: pip install -e .[test]
- name: Run MyPy
run: mypy pyusps
build_dist:
name: Build wheels
needs: [test, lint]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- name: Build sdist and wheel
run: |
pip install build
python -m build
- uses: actions/upload-artifact@v2
with:
name: dist
path: dist/
- name: Publish wheels to PyPI
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
pip install twine
twine upload dist/*
continue-on-error: true
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# CHANGELOG

## 1.0.0

See https://github.com/thelinuxkid/pyusps/issues/10 for
the motivation of many of these changes.

### Changed

- Drop support for anythin below Python 3.6
- Main signature changes from `verify(user_id, *args)` to `verify(user_id, args)`
You must always supply an iterable of inputs. Single inputs are no longer supported,
just wrap them in a length-one list if you need. Always returns a list of
`dicts`/`USPSError`s.
- Instead of `OrderedDict`s, we just return plain old `dict`s.
- Changed returned/raised `ValueError`s from not-found addresses to be always-returned
`USPSError`s
- Instead of `TypeError` or `ValueError` from parsing errors, we now consistently raise
`RuntimeError`s
- Removed `api_url` from global namespace. IDK why you would have relied on this though.

### Added

- Supports supplying an iterable of addresses, no longer needs the __len__ method.
- Each supplied input now only needs to have a `__getitem__()` method. Before, it
needed that but also a `get()` method.
- If you supply an empty iterable as input, you get back an empty list, not an error.
- The new `USPSError` includes the attributes `code: str` and `description: str`
so you can get the original error without having to do string parsing.
- Testing on GitHub Actions!
- Type hints
2 changes: 0 additions & 2 deletions MANIFEST.in

This file was deleted.

150 changes: 54 additions & 96 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ or easy_install::
Address Information API
=======================

This API is avaiable via the pyusps.address_information.verify
function. It takes in the user ID given to you by the USPS
and a variable length list of addresses to verify.
This API is avaiable via the `pyusps.address_information.verify`
function.

Requests
--------

Each address is a dict containing the following required keys:
It takes in the user ID given to you by the USPS and an iterable of addresses to verify.
You can only supply up to 5 addresses at a time due to the API's limits.
Each address is a dict-like (e.g. supports `__getitem__()`) containing the following
required keys:

:address: The street address
:city: The city
Expand All @@ -44,13 +46,10 @@ The following keys are optional:
:address_extended: An apartment, suite number, etc
:urbanization: For Puerto Rico addresses only



Responses
---------

The response will either be a dict, if a single address was requested,
or a list of dicts, if multiple addresses were requested. Each address
The response will either be list of `dict`s or `USPSError`s. Each `dict`
will always contain the following keys:

:address: The street address
Expand All @@ -60,7 +59,7 @@ will always contain the following keys:
:zip4: The last four numbers of the zip code


Each address can optionally contain the following keys:
Each `dict` can optionally contain the following keys:

:firm_name: The company name, e.g., XYZ Corp.
:address_extended: An apartment, suite number, etc
Expand All @@ -70,101 +69,60 @@ Each address can optionally contain the following keys:
*firm_name, address_extended and urbanization will return the value
requested if the API does not find a match.*

For multiple addresses, the order in which the addresses
If the USPS can't find an address, then in the response list, instead of a `dict` you
will receive a `USPSError`. `USPSError` is a subclass of `RuntimeError`, and has the
additional attributes of `code` and `description` for the error.

The order in which the addresses
were specified in the request is preserved in the response.

Errors
------

A ValueError will be raised if there's a general error, e.g.,
invalid user id, or if a single address request generates an error.
Except for a general error, multiple addresses requests do not raise errors.
Instead, if one of the addresses generates an error, the
ValueError object is returned along with the rest of the results.
- ValueError will be raised if you request more than 5 addresses.
- RuntimeError will be raised when the API returns a response that we can't parse
or otherwise doesn't make sense (You shouldn't run into this).
- A USPSError will be raised if the supplied user_id is invalid.

Example
-------

Examples
--------
Mutiple addresses, one of them isn't found so an error is returned::

>>> from pyusps import address_information

>>> addrs = [
{
"address": "6406 Ivy Lane",
"city": "Greenbelt",
"state": "MD",
},
{
"address": "8 Wildwood Drive",
"city": "Old Lyme",
"state": "NJ",
},
]
>>> results = address_information.verify('foo_id', addrs)
>>> results
[
{
'address': '6406 IVY LN',
'city': 'GREENBELT',
'returntext': 'Default address: The address you entered was found but more '
'information is needed (such as an apartment, suite, or box '
'number) to match to a specific address.',
'state': 'MD',
'zip4': '1435',
'zip5': '20770'
},
USPSError('-2147219400: Invalid City.'),
]
>>> results[1].code
'-2147219400'
>>> res[1].description
'Invalid City.'

Single address request::

from pyusps import address_information

addr = dict([
('address', '6406 Ivy Lane'),
('city', 'Greenbelt'),
('state', 'MD'),
])
address_information.verify('foo_id', addr)
dict([
('address', '6406 IVY LN'),
('city', 'GREENBELT'),
('state', 'MD'),
('zip5', '20770'),
('zip4', '1441'),
])

Mutiple addresses request::

from pyusps import address_information

addrs = [
dict([
('address', '6406 Ivy Lane'),
('city', 'Greenbelt'),
('state', 'MD'),
]),
dict([
('address', '8 Wildwood Drive'),
('city', 'Old Lyme'),
('state', 'CT'),
]),
]
address_information.verify('foo_id', *addrs)
[
dict([
('address', '6406 IVY LN'),
('city', 'GREENBELT'),
('state', 'MD'),
('zip5', '20770'),
('zip4', '1441'),
]),
dict([
('address', '8 WILDWOOD DR'),
('city', 'OLD LYME'),
('state', 'CT'),
('zip5', '06371'),
('zip4', '1844'),
]),
]

Mutiple addresses error::

from pyusps import address_information

addrs = [
dict([
('address', '6406 Ivy Lane'),
('city', 'Greenbelt'),
('state', 'MD'),
]),
dict([
('address', '8 Wildwood Drive'),
('city', 'Old Lyme'),
('state', 'NJ'),
]),
]
address_information.verify('foo_id', *addrs)
[
dict([
('address', '6406 IVY LN'),
('city', 'GREENBELT'),
('state', 'MD'),
('zip5', '20770'),
('zip4', '1441'),
]),
ValueError('-2147219400: Invalid City. '),
]

Reference
---------
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools>=43.0.0", "wheel"]
build-backend = "setuptools.build_meta"
Loading