diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml new file mode 100644 index 0000000..fa7ab8a --- /dev/null +++ b/.github/workflows/pythonpackage.yml @@ -0,0 +1,31 @@ +name: Python package + +on: [push] + +jobs: + build: + + runs-on: ${{ matrix.os }} + strategy: + max-parallel: 4 + matrix: + python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + os: [ubuntu-latest, macos-latest] + + steps: + - uses: actions/checkout@v1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Lint with flake8 + run: | + pip install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with Nose + run: | + pip install tox tox-gh-actions + tox diff --git a/example.py b/example.py index d6f1358..6cc9c08 100644 --- a/example.py +++ b/example.py @@ -21,13 +21,14 @@ #To create an item item = { - "fields":[ - {"external_id":"org-name", "values":[{"value":"The Items API sucks"}]} - ] + "fields":[ + {"external_id":"org-name", "values":[{"value":"The Items API sucks"}]} + ] } -#print c.Application.find(179652) + +app_id = c.Application.find(179652) c.Item.create(app_id, item) - + #Undefined and created at runtime example #print c.transport.GET.user.status() diff --git a/pypodio2/adapters.py b/pypodio2/adapters.py index 7b1b2e6..81c0b86 100644 --- a/pypodio2/adapters.py +++ b/pypodio2/adapters.py @@ -1,3 +1,4 @@ +from __future__ import print_function import json diff --git a/pypodio2/areas.py b/pypodio2/areas.py index cfd60f0..55c6fb9 100644 --- a/pypodio2/areas.py +++ b/pypodio2/areas.py @@ -1,10 +1,18 @@ # -*- coding: utf-8 -*- +from future import standard_library +standard_library.install_aliases() +from builtins import str +from builtins import object import json try: from urllib.parse import urlencode except ImportError: - from urllib import urlencode + from urllib.parse import urlencode + + +class ApiErrorException(Exception): + pass class Area(object): diff --git a/pypodio2/client.py b/pypodio2/client.py index 8689911..ef6e343 100644 --- a/pypodio2/client.py +++ b/pypodio2/client.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from builtins import object from . import areas diff --git a/pypodio2/encode.py b/pypodio2/encode.py index 6b13922..ea38c0f 100644 --- a/pypodio2/encode.py +++ b/pypodio2/encode.py @@ -5,10 +5,16 @@ multipart/form-data is the standard way to upload files over HTTP""" +from past.builtins import cmp +from future import standard_library +standard_library.install_aliases() +from builtins import next +from builtins import str +from builtins import object import mimetypes import os import re -import urllib +import urllib.request, urllib.parse, urllib.error from email.header import Header __all__ = ['gen_boundary', 'encode_and_quote', 'MultipartParam', @@ -44,9 +50,9 @@ def encode_and_quote(data): if data is None: return None - if isinstance(data, unicode): + if isinstance(data, str): data = data.encode("utf-8") - return urllib.quote_plus(data) + return urllib.parse.quote_plus(data) def _strify(s): @@ -54,7 +60,7 @@ def _strify(s): otherwise return str(s), or None if s is None""" if s is None: return None - if isinstance(s, unicode): + if isinstance(s, str): return s.encode("utf-8") return str(s) @@ -99,7 +105,7 @@ def __init__(self, name, value=None, filename=None, filetype=None, if filename is None: self.filename = None else: - if isinstance(filename, unicode): + if isinstance(filename, str): # Encode with XML entities self.filename = filename.encode("ascii", "xmlcharrefreplace") else: @@ -166,7 +172,7 @@ def from_params(cls, params): MultipartParam object names must match the given names in the name,value pairs or mapping, if applicable.""" if hasattr(params, 'items'): - params = params.items() + params = list(params.items()) retval = [] for item in params: @@ -323,13 +329,13 @@ def get_headers(params, boundary): """Returns a dictionary with Content-Type and Content-Length headers for the multipart/form-data encoding of ``params``.""" headers = {} - boundary = urllib.quote_plus(boundary) + boundary = urllib.parse.quote_plus(boundary) headers['Content-Type'] = "multipart/form-data; boundary=%s" % boundary headers['Content-Length'] = str(get_body_size(params, boundary)) return headers -class MultipartYielder: +class MultipartYielder(object): def __init__(self, params, boundary, cb): self.params = params self.boundary = boundary @@ -344,12 +350,12 @@ def __init__(self, params, boundary, cb): def __iter__(self): return self - def next(self): + def __next__(self): """generator function to yield multipart/form-data representation of parameters""" if self.param_iter is not None: try: - block = self.param_iter.next() + block = next(self.param_iter) self.current += len(block) if self.cb: self.cb(self.p, self.current, self.total) @@ -373,7 +379,7 @@ def next(self): self.p = self.params[self.i] self.param_iter = self.p.iter_encode(self.boundary) self.i += 1 - return self.next() + return next(self) def reset(self): self.i = 0 @@ -425,7 +431,7 @@ def multipart_encode(params, boundary=None, cb=None): if boundary is None: boundary = gen_boundary() else: - boundary = urllib.quote_plus(boundary) + boundary = urllib.parse.quote_plus(boundary) headers = get_headers(params, boundary) params = MultipartParam.from_params(params) diff --git a/pypodio2/transport.py b/pypodio2/transport.py index a19d77f..a4e8e00 100644 --- a/pypodio2/transport.py +++ b/pypodio2/transport.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- +from future import standard_library +standard_library.install_aliases() +from builtins import str +from builtins import object from httplib2 import Http try: from urllib.parse import urlencode except ImportError: - from urllib import urlencode + from urllib.parse import urlencode from .encode import multipart_encode diff --git a/setup.py b/setup.py index 5d5f299..f59959c 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,27 @@ from setuptools import setup +extras = { + 'test': [ + 'mock', + 'nose', + 'tox', + ] +} + setup( name="pypodio2", - version="0.2", + version="1.0.0b0", description="Python wrapper for the Podio API", author="Podio", author_email="mail@podio.com", url="https://github.com/podio/podio-py", license="MIT", packages=["pypodio2"], - install_requires=["httplib2"], - tests_require=["nose", "mock", "tox"], + install_requires=[ + "httplib2", + "future", + ], + extras_require=extras, test_suite="nose.collector", classifiers=[ "Development Status :: 4 - Beta", diff --git a/tox.ini b/tox.ini index 93db941..8f8eb14 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,14 @@ [tox] -envlist = py27,py35 +envlist = py27,py35,py36,py37,py38 + +[gh-actions] +python = + 2.7: py27 + 3.5: py35 + 3.6: py36 + 3.7: py37 + 3.8: py38 [testenv] -commands = {envpython} setup.py nosetests -deps = - nose - mock - httplib2 +extras = test +commands = {posargs:nosetests}