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

PXP-459 Futurize/modernize (make it python 2/3 compatible) #16

Merged
merged 27 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ce525ee
Bump version to 1.3.dev0
yonran Oct 9, 2023
6c3c25d
Fix a couple doctests
yonran Oct 6, 2023
25cb372
Fix urlencode parameter type (errors found by mypy)
yonran Oct 4, 2023
01c49bd
Upgrade to python 2.7
yonran Oct 9, 2023
cf5c5a0
Remove the obsolete `requires` keyword in setup
yonran Oct 2, 2023
9fbba40
Use exact versions in requirements.txt
yonran Oct 2, 2023
de77901
Delete unused tests/requirements.txt
yonran Oct 3, 2023
83a1df8
Add github workflow tests.yml
yonran Oct 4, 2023
e08d099
Add test that requirements.txt is complete
yonran Oct 4, 2023
b3bba78
Remove nose; use python -m unittest
yonran Oct 3, 2023
4b48095
Fix flake8 F401 module imported but unused
yonran Oct 4, 2023
8ebdf79
Fix flake8 F821 undefined name errmsg
yonran Oct 4, 2023
b5c89e8
Fix flake8 F632 use ==/!= to compare constant literals
yonran Sep 27, 2021
59fe4a1
Fix pycodestyle E722 do not use bare 'except'
yonran Oct 4, 2023
94aa4af
Fix pycodestyle E721 do not compare types
yonran Oct 5, 2023
ad11049
Fix pycodestyle E401,W291,W391,W293
yonran Oct 17, 2023
9c535ab
Run flake8 from github workflow
yonran Oct 17, 2023
76114e7
Switch from mox to mock
yonran Sep 28, 2021
416747a
Add test that filter takes both str and unicode
yonran Sep 28, 2021
ca1001b
Fix deprecated TestCase methods
yonran Jul 10, 2023
f9bee5c
Call urlparse.parse_qs instead of cgi.parse_qs
yonran Sep 28, 2021
d53fea1
Add tests of PageOf and ListOf
yonran Oct 9, 2023
3245056
Add support for python3
yonran Sep 26, 2021
51bf21a
python3: fix pickle error in unit test
yonran Sep 28, 2021
577d638
python3: decode string with replace before json.loads
yonran Sep 29, 2021
2a2ce87
python3 fix: make ListObject parameter order match unit test
yonran Sep 28, 2021
532fba7
Add test that lone surrogates are not detected or fixed
yonran Sep 28, 2021
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
36 changes: 36 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[flake8]
exclude =
.git,
.direnv,
# flake8 has extend-ignore, but autopep8 does not support it
ignore =
# python3 -m flake8 --help (3.9.2, with no .flake8 config)
# gives these defaults:
# E704 multiple statements on one line (def)
E704,
# E24 multiple spaces after ‘,’ or tab after ‘,’
E24,
# E226 missing whitespace around arithmetic operator
E226,
# E126 continuation line over-indented for hanging indent
E126,
# W503 line break before binary operator
W503,
# W504 line break after binary operator
W504,
# E121 continuation line under-indented for hanging indent
E121,
# E123 closing bracket does not match indentation of opening bracket’s line
E123,

# too many changes for now
# E501 line too long
E501,
# fixing style is too many changes for now
# https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes
# E1 Indentation
E1,
# E2 Whitespace
E2,
# E3 Blank line
E3,
64 changes: 64 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Tests

on: [push, pull_request]

permissions:
contents: read

jobs:
build:
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.8", "pypy-3.9"]
os: [ubuntu-20.04, ubuntu-22.04, macOS-latest, windows-latest]
include:
- python-version: "2.7"
os: "ubuntu-20.04"
- python-version: "2.7"
os: "ubuntu-22.04"
steps:
- uses: actions/[email protected]
- name: Set up Python ${{ matrix.python-version }} (github action)
if: matrix.python-version != '2.7'
uses: actions/[email protected]
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Set up Python ${{ matrix.python-version }} (apt-get)
if: matrix.python-version == '2.7'
# since actions/python-versions removed 2.7 from versions-manifest.json
# and also deleted all the python 2.7 binary artifacts,
# we have to apt-get install python2
# https://github.com/actions/setup-python/issues/672
run: |
set -eux
sudo apt-get update
sudo apt-get install -y python2 python3-virtualenv
virtualenv -p python2 "${{ runner.temp }}/venv"
# Fix for error in ubuntu-20.04 pip (fixed in ubuntu-22.04)
# can't find '__main__' module in '/usr/share/python-wheels/pep517-0.8.2-py2.py3-none-any.whl/pep517/_in_process.py
# https://github.com/pypa/pip/issues/7874#issuecomment-605520503
"${{ runner.temp }}/venv/bin/pip" install --force-reinstall --upgrade pip
echo "${{ runner.temp }}/venv/bin" >> $GITHUB_PATH
- name: Install prod dependencies
run: python -m pip install -r requirements.txt
- name: Check that all recursive dependencies are in requirements.txt
run: python -m unittest tests.test_requirements_txt
env:
TEST_REQUIREMENTS: prod
- name: Install test dependencies
run: python -m pip install -r requirements-test.txt
- name: Run tests
run: python -m unittest discover
- name: Check that all recursive dependencies are in requirements.txt and requirements-test.txt
run: python -m unittest tests.test_requirements_txt
env:
TEST_REQUIREMENTS: test
- name: Run flake8
# need to upgrade flake8 to 6.1.0 for python 3.12
# https://flake8.pycqa.org/en/latest/release-notes/6.1.0.html
if: matrix.python-version != '3.12'
run: python -m flake8
3 changes: 2 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os
import sys
import os

# 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
Expand Down
2 changes: 0 additions & 2 deletions doc/exts/document_init_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
"""


import logging

def document_init_methods(app, what, name, obj, skip, options):
if not skip:
return
Expand Down
2 changes: 1 addition & 1 deletion doc/readme_from_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

import remoteobjects

readme = file('README.rst', 'w')
readme = open('README.rst', 'w')
readme.write(remoteobjects.__doc__.strip())
readme.write("\n")
readme.close()
21 changes: 11 additions & 10 deletions examples/couchdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,18 @@
A generic example CouchDB client, implemented using remoteobjects.

"""
from __future__ import print_function

__version__ = '1.0'
__date__ = '24 August 2009'
__author__ = 'Mark Paschal'


import httplib
from optparse import OptionParser
import simplejson as json
import sys
from urlparse import urljoin, urlparse
from six.moves import http_client
from six.moves.urllib.parse import urljoin

from remoteobjects import RemoteObject, fields, ListObject

Expand All @@ -53,10 +54,10 @@ class CouchObject(RemoteObject):

# CouchDB omits Locations for Created responses, so don't expect them.
location_headers = dict(RemoteObject.location_headers)
location_headers[httplib.CREATED] = None
location_headers[http_client.CREATED] = None

def update_from_response(self, url, response, content):
if response.status == httplib.CREATED:
if response.status == http_client.CREATED:
# CouchDB CREATED responses don't contain full content, so only
# unpack the ID and revision.
data = json.loads(content)
Expand Down Expand Up @@ -103,7 +104,7 @@ class ViewResult(CouchObject, ListObject):
entries = fields.List(fields.Object(ListItem), api_name='rows')

def filter(self, **kwargs):
for k, v in kwargs.iteritems():
for k, v in kwargs.items():
if isinstance(v, list) or isinstance(v, dict) or isinstance(v, bool):
kwargs[k] = json.dumps(v)
return super(ViewResult, self).filter(**kwargs)
Expand All @@ -127,16 +128,16 @@ def create_db(url):
db.deliver()
except Database.NotFound:
db.put()
print "Database %s created" % url
print("Database %s created" % url)
else:
print "Database %s already exists" % url
print("Database %s already exists" % url)


def create_view(dburl):
viewset = Viewset.get(urljoin(dburl, '_design/profiles'))
try:
viewset.deliver()
print "Retrieved existing 'profiles' views"
print("Retrieved existing 'profiles' views")
except Viewset.NotFound:
# Start with an empty set of views.
viewset.views = {}
Expand All @@ -150,7 +151,7 @@ def create_view(dburl):
viewset.views['url'] = View(mapfn=profiles_url_code)

viewset.put()
print "Updated 'profiles' views for %s" % dburl
print("Updated 'profiles' views for %s" % dburl)


def main(argv=None):
Expand All @@ -164,7 +165,7 @@ def main(argv=None):

db = opts.database
if db is None:
print >>sys.stderr, "Option --database is required"
print("Option --database is required", file=sys.stderr)
return 1

# Create the database, if necessary.
Expand Down
27 changes: 13 additions & 14 deletions examples/giantbomb.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,18 @@
An example Giant Bomb API client, implemented using remoteobjects.

"""
from __future__ import print_function

__version__ = '1.0'
__date__ = '24 August 2009'
__author__ = 'Mark Paschal'


from cgi import parse_qs
from datetime import datetime
from optparse import OptionParser
import sys
import time
from urllib import urlencode
from urlparse import urljoin, urlparse, urlunparse
from six.moves.urllib.parse import (
urlencode, parse_qs, urljoin, urlparse, urlunparse
)

from remoteobjects import RemoteObject, fields

Expand All @@ -69,9 +68,9 @@ def filter(self, **kwargs):
url = self._location
parts = list(urlparse(url))
query = parse_qs(parts[4])
query = dict([(k, v[0]) for k, v in query.iteritems()])
query = dict([(k, v[0]) for k, v in query.items()])

for k, v in kwargs.iteritems():
for k, v in kwargs.items():
if v is None and k in query:
del query[k]
else:
Expand Down Expand Up @@ -144,7 +143,7 @@ def main(argv=None):
opts, args = parser.parse_args()

if opts.key is None:
print >>sys.stderr, "Option --key is required"
print("Option --key is required", file=sys.stderr)
return 1

query = ' '.join(args)
Expand All @@ -155,16 +154,16 @@ def main(argv=None):
search = search.filter(query=query)

if len(search.results) == 0:
print "No results for %r" % query
print("No results for %r" % query)
elif len(search.results) == 1:
(game,) = search.results
print "## %s ##" % game.name
print
print game.summary
print("## %s ##" % game.name)
print()
print(game.summary)
else:
print "## Search results for %r ##" % query
print("## Search results for %r ##" % query)
for game in search.results:
print game.name
print(game.name)

return 0

Expand Down
30 changes: 14 additions & 16 deletions examples/netflix.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,21 @@
An example Netflix API client, implemented using remoteobjects.

"""
from __future__ import print_function

__version__ = '1.0'
__date__ = '25 August 2009'
__author__ = 'Mark Paschal'


import cgi
from optparse import OptionParser
import sys
from urllib import urlencode
import urlparse
from six.moves.urllib.parse import parse_qs, urlparse
from xml.etree import ElementTree

import httplib2
from oauth.oauth import OAuthConsumer, OAuthRequest, OAuthSignatureMethod_HMAC_SHA1

from remoteobjects import RemoteObject, fields, PageObject
from remoteobjects import RemoteObject, fields


class Flixject(RemoteObject):
Expand All @@ -68,9 +66,9 @@ def get_request(self, headers=None, **kwargs):
http_url=request['uri'])

# OAuthRequest will strip our query parameters, so add them back in.
parts = list(urlparse.urlparse(self._location))
queryargs = cgi.parse_qs(parts[4], keep_blank_values=True)
for key, value in queryargs.iteritems():
parts = list(urlparse(self._location))
queryargs = parse_qs(parts[4], keep_blank_values=True)
for key, value in queryargs.items():
orq.set_parameter(key, value[0])

# Sign the request.
Expand All @@ -86,7 +84,7 @@ def get_request(self, headers=None, **kwargs):
return request

def update_from_tree(self, tree):
data = dict((k, v(tree)) for k, v in self.decoder_ring.items())
data = dict((k, v(tree)) for k, v in list(self.decoder_ring.items()))
self.update_from_dict(data)
return self

Expand Down Expand Up @@ -135,18 +133,18 @@ def do_search(opts, args):
search.deliver()

if len(search.results) == 0:
print "No results for %r" % query
print("No results for %r" % query)
elif len(search.results) == 1:
result = search.results[0]
print "## %s ##" % result.title
print("## %s ##" % result.title)
else:
print "## Results for %r ##" % query
print
print("## Results for %r ##" % query)
print()
for title in search.results:
if title is None:
print "(oops, none)"
print("(oops, none)")
else:
print title.title
print(title.title)

return 0

Expand All @@ -166,7 +164,7 @@ def main(argv=None):
opts, args = parser.parse_args()

if opts.key is None or opts.secret is None:
print >>sys.stderr, "Options --key and --secret are required"
print("Options --key and --secret are required", file=sys.stderr)
return 1

Flixject.api_token = (opts.key, opts.secret)
Expand Down
Loading