Skip to content

Commit

Permalink
preparing for full python 2 and 3 support
Browse files Browse the repository at this point in the history
  • Loading branch information
wolph committed Jun 7, 2016
1 parent f225558 commit e0f9bbe
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 90 deletions.
11 changes: 6 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
build
dist
python_utils.egg-info
docs/_build
cover
/build
/dist
/*.egg-info
/docs/_build
/cover
/.eggs
52 changes: 43 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,49 @@
sudo: false
language: python
python:
- "2.6"
- "2.7"
# command to install dependencies

env:
global:
- PIP_WHEEL_DIR=$HOME/.wheels
- PIP_FIND_LINKS=file://$PIP_WHEEL_DIR

matrix:
include:
- python: '2.7'
env: TOXENV=docs
- python: '2.7'
env: TOXENV=flake8
- python: '2.7'
env: TOXENV=py27
- python: '3.3'
env: TOXENV=py33
- python: '3.4'
env: TOXENV=py34
- python: '3.5'
env: TOXENV=py35
- python: 'pypy'
env: TOXENV=pypy
- python: 'pypy3'
env: TOXENV=pypy3

cache:
directories:
- $HOME/.wheels

# command to install dependencies, e.g. pip install -r requirements.txt
install:
- pip install .
- pip install coveralls
# command to run tests
- mkdir -p $PIP_WHEEL_DIR
- pip wheel -r tests/requirements.txt
- pip install -e .
- pip install tox coveralls

script:
- nosetests --with-coverage
- tox

after_success:
- coveralls
- coveralls

notifications:
email:
on_success: never
on_failure: change

35 changes: 26 additions & 9 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# 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.
sys.path.insert(0, os.path.abspath('..'))
import python_utils
from python_utils import __about__

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

Expand Down Expand Up @@ -53,17 +53,34 @@
project = u'Python Utils'
copyright = u'%s, <a href="http://wol.ph/">%s</a>' % (
datetime.date.today().year,
python_utils.__author__,
__about__.__author__,
)

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = python_utils.__version__
version = __about__.__version__
# The full version, including alpha/beta/rc tags.
release = python_utils.__version__
release = __about__.__version__

# Monkey patch to disable nonlocal image warning
import sphinx
original_warn_mode = sphinx.environment.BuildEnvironment.warn_node


def allow_nonlocal_image_warn_node(self, msg, *args, **kwargs):
if not msg.startswith('nonlocal image URI found:'):
original_warn_mode(self, msg, *args, **kwargs)

sphinx.environment.BuildEnvironment.warn_node = allow_nonlocal_image_warn_node

suppress_warnings = [
'image.nonlocal_uri',
]

needs_sphinx = '1.4'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down Expand Up @@ -197,7 +214,7 @@
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'PythonUtils.tex', u'Python Utils Documentation',
python_utils.__author__, 'manual'),
__about__.__author__, 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -227,7 +244,7 @@
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'pythonutils', u'Python Utils Documentation',
[python_utils.__author__], 1)
[__about__.__author__], 1)
]

# If true, show URL addresses after external links.
Expand All @@ -241,7 +258,7 @@
# dir menu entry, description, category)
texinfo_documents = [
('index', 'PythonUtils', u'Python Utils Documentation',
python_utils.__author__, 'PythonUtils', python_utils.__description__,
__about__.__author__, 'PythonUtils', __about__.__description__,
'Miscellaneous'),
]

Expand All @@ -259,8 +276,8 @@

# Bibliographic Dublin Core info.
epub_title = u'Python Utils'
epub_author = python_utils.__author__
epub_publisher = python_utils.__author__
epub_author = __about__.__author__
epub_publisher = __about__.__author__
epub_copyright = copyright

# The language of the text. It defaults to the language option
Expand Down
3 changes: 3 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mock
sphinx
python-utils
12 changes: 6 additions & 6 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ python_files =

addopts =
--cov python_utils
--cov-report term-missing
--cov-report html
--cov-report term-missing
--doctest-modules
--pep8
--flakes
python_utils
tests
--pep8
--flakes

pep8ignore =
*.py W391
Expand All @@ -20,4 +18,6 @@ pep8ignore =
flakes-ignore =
docs/*.py ALL


doctest_optionflags =
ALLOW_UNICODE
ALLOW_BYTES
8 changes: 8 additions & 0 deletions python_utils/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__version__ = '2.0.0'
__author__ = 'Rick van Hattem'
__author_email__ = 'Wolph@wol.ph'
__description__ = (
'Python Utils is a module with some convenient utilities not included '
'with the standard Python install')
__url__ = 'https://github.com/WoLpH/python-utils'

7 changes: 0 additions & 7 deletions python_utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +0,0 @@
__version__ = '1.6.2'
__author__ = 'Rick van Hattem'
__author_email__ = 'Rick.van.Hattem@Fawo.nl'
__description__ = (
'Python Utils is a module with some convenient utilities not included '
'with the standard Python install')

Empty file added python_utils/compat.py
Empty file.
43 changes: 27 additions & 16 deletions python_utils/converters.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)

import re
import six


def to_int(input_, default=0, exception=(ValueError, TypeError), regexp=None):
Expand Down Expand Up @@ -57,17 +61,17 @@ def to_int(input_, default=0, exception=(ValueError, TypeError), regexp=None):
>>> to_int('abc', regexp=123)
Traceback (most recent call last):
...
TypeError: unknown argument for regexp parameter
TypeError: unknown argument for regexp parameter: 123
'''

if regexp is True:
regexp = re.compile('(\d+)')
elif isinstance(regexp, str):
elif isinstance(regexp, six.string_types):
regexp = re.compile(regexp)
elif hasattr(regexp, 'search'):
pass
elif regexp is not None:
raise TypeError('unknown argument for regexp parameter')
raise TypeError('unknown argument for regexp parameter: %r' % regexp)

try:
if regexp:
Expand All @@ -82,7 +86,7 @@ def to_int(input_, default=0, exception=(ValueError, TypeError), regexp=None):
def to_float(input_, default=0, exception=(ValueError, TypeError),
regexp=None):
'''
Convert the given input_ to an integer or return default
Convert the given `input_` to an integer or return default
When trying to convert the exceptions given in the exception parameter
are automatically catched and the default will be returned.
Expand Down Expand Up @@ -135,7 +139,7 @@ def to_float(input_, default=0, exception=(ValueError, TypeError),

if regexp is True:
regexp = re.compile('(\d+(\.\d+|))')
elif isinstance(regexp, str):
elif isinstance(regexp, six.string_types):
regexp = re.compile(regexp)
elif hasattr(regexp, 'search'):
pass
Expand All @@ -158,20 +162,22 @@ def to_unicode(input_, encoding='utf-8', errors='replace'):
:rtype: unicode
>>> to_unicode(b'a')
'a'
>>> to_unicode('a')
u'a'
'a'
>>> to_unicode(u'a')
u'a'
'a'
>>> class Foo(object): __str__ = lambda s: u'a'
>>> to_unicode(Foo())
u'a'
'a'
>>> to_unicode(Foo)
u"<class 'python_utils.converters.Foo'>"
"<class 'python_utils.converters.Foo'>"
'''
if isinstance(input_, str):
if hasattr(input_, 'decode'):
input_ = input_.decode(encoding, errors)
else:
input_ = unicode(input_)
input_ = six.text_type(input_)
return input_


Expand All @@ -181,19 +187,24 @@ def to_str(input_, encoding='utf-8', errors='replace'):
:rtype: str
>>> to_str('a')
'a'
b'a'
>>> to_str(u'a')
'a'
b'a'
>>> to_str(b'a')
b'a'
>>> class Foo(object): __str__ = lambda s: u'a'
>>> to_str(Foo())
'a'
>>> to_str(Foo)
"<class 'python_utils.converters.Foo'>"
'''
if isinstance(input_, str):
input_ = input_.encode(encoding, errors)
if isinstance(input_, six.binary_type):
pass
else:
input_ = str(input_)
if not hasattr(input_, 'encode'):
input_ = six.text_type(input_)

input_ = input_.encode(encoding, errors)
return input_


4 changes: 2 additions & 2 deletions python_utils/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ def timesince(dt, default='just now'):

output = []
for period, singular, plural in periods:
if period:
if period == 1:
if int(period):
if int(period) == 1:
output.append('%d %s' % (period, singular))
else:
output.append('%d %s' % (period, plural))
Expand Down
29 changes: 20 additions & 9 deletions python_utils/import_.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@

class DummyException(Exception):
pass


def import_global(
name, modules=None, exceptions=None, locals_=None, globals_=None,
level=-1):
name, modules=None, exceptions=DummyException, locals_=None,
globals_=None, level=-1):
'''Import the requested items into the global scope
WARNING! this method _will_ overwrite your global scope
If you have a variable named "path" and you call import_global('sys')
it will be overwritten with sys.path
name -- the name of the module to import, e.g. sys
modules -- the modules to import, use None for everything
exception -- the exception to catch, e.g. ImportError
locals_ -- the `locals()` method (in case you need a different scope)
globals_ -- the `globals()` method (in case you need a different scope)
level -- the level to import from, this can be used for relative imports
Args:
name (str): the name of the module to import, e.g. sys
modules (str): the modules to import, use None for everything
exception (Exception): the exception to catch, e.g. ImportError
`locals_`: the `locals()` method (in case you need a different scope)
`globals_`: the `globals()` method (in case you need a different scope)
level (int): the level to import from, this can be used for
relative imports
'''
frame = None
try:
Expand All @@ -38,8 +43,14 @@ def import_global(
name = name[1:]
level = 1

# raise IOError((name, level))
module = __import__(
name[0] or '.', globals_, locals_, name[1:], level=level)
name=name[0] or '.',
globals=globals_,
locals=locals_,
fromlist=name[1:],
level=max(level, 0),
)

# Make sure we get the right part of a dotted import (i.e.
# spam.eggs should return eggs, not spam)
Expand Down
Loading

0 comments on commit e0f9bbe

Please sign in to comment.