diff --git a/CHANGES.txt b/CHANGES.txt index eae3b6221..76991fe2e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,15 @@ Chaco CHANGELOG =============== +Release 4.7.1 +------------- + +Fixes + +* Avoid datetime issue on Windows 10 (PR#387) +* Fix doc build (PR#384) +* Upcast to int_ instead of int64 to avoid bincount issue (PR#383) + Release 4.7.0 ------------- diff --git a/chaco/scales/safetime.py b/chaco/scales/safetime.py index 288069d18..60007e1f0 100644 --- a/chaco/scales/safetime.py +++ b/chaco/scales/safetime.py @@ -12,7 +12,14 @@ + ['safe_fromtimestamp', 'datetime', 'timedelta', 'MINYEAR', 'MAXYEAR', 'EPOCH']) -EPOCH = datetime.fromtimestamp(0.0) + +# On Windows 10, datetime.fromtimestamp fails with an OSError for timestamps +# less than 86400s (1 day). We work around this by initializing the epoch to +# some time past that, and then going back that many seconds to arrive at "time +# 0". See the discussion in GH #376 (as well as Python issue 29097). +DAY_SECONDS = 24 * 60 * 60 +EPOCH = datetime.fromtimestamp(DAY_SECONDS) - timedelta(seconds=DAY_SECONDS) + # Can't monkeypatch methods of anything in datetime, so we have to wrap them def safe_fromtimestamp(timestamp, *args, **kwds): diff --git a/chaco/tests/test_image_plot.py b/chaco/tests/test_image_plot.py index 1df5de21e..80cfb384d 100644 --- a/chaco/tests/test_image_plot.py +++ b/chaco/tests/test_image_plot.py @@ -81,11 +81,12 @@ def calculate_rms(image_result, expected_image): """ # calculate the per-pixel errors, then compute the root mean square error num_values = np.prod(expected_image.shape) - # Cast to int64 to reduce likelihood of over-/under-flow. - abs_diff_image = abs(np.int64(expected_image) - np.int64(image_result)) + # Images may be e.g. 8-bit unsigned integer; upcast to default integer size + # (32 or 64 bit) to reduce likelihood of over-/under-flow. + abs_diff_image = abs(np.int_(expected_image) - np.int_(image_result)) histogram = np.bincount(abs_diff_image.ravel(), minlength=256) - sum_of_squares = np.sum(histogram * np.arange(len(histogram))**2) + sum_of_squares = np.sum(np.int64(histogram) * np.arange(len(histogram))**2) rms = np.sqrt(float(sum_of_squares) / num_values) return rms diff --git a/chaco/tools/dataprinter.py b/chaco/tools/dataprinter.py index 023019feb..141c9e825 100644 --- a/chaco/tools/dataprinter.py +++ b/chaco/tools/dataprinter.py @@ -38,8 +38,8 @@ def normal_mouse_move(self, event): y = plot.value.get_data()[ndx] print(self.format % (x,y)) else: - print("dataprinter: don't know how to handle plots of type", end=" ") - print(plot.__class__.__name__) + print("dataprinter: don't know how to handle plots of type {}".format( + plot.__class__.__name__)) return diff --git a/docs/source/sphinxext/comment_eater.py b/docs/source/sphinxext/comment_eater.py index 715748cad..cb408cd97 100644 --- a/docs/source/sphinxext/comment_eater.py +++ b/docs/source/sphinxext/comment_eater.py @@ -1,11 +1,10 @@ -import six.moves as sm - +from cStringIO import StringIO import compiler import inspect import textwrap import tokenize -from .compiler_unparse import unparse +from compiler_unparse import unparse class Comment(object): @@ -77,7 +76,7 @@ def __init__(self): def process_file(self, file): """ Process a file object. """ - for token in tokenize.generate_tokens(sm.next(file)): + for token in tokenize.generate_tokens(file.next): self.process_token(*token) self.make_index() @@ -155,7 +154,7 @@ def get_class_traits(klass): # FIXME: gracefully handle errors here or in the caller? source = inspect.getsource(klass) cb = CommentBlocker() - cb.process_file(sm.StringIO(source)) + cb.process_file(StringIO(source)) mod_ast = compiler.parse(source) class_ast = mod_ast.node.nodes[0] for node in class_ast.code.nodes: diff --git a/docs/source/sphinxext/compiler_unparse.py b/docs/source/sphinxext/compiler_unparse.py index 1bb1e13fb..d62d65c2e 100644 --- a/docs/source/sphinxext/compiler_unparse.py +++ b/docs/source/sphinxext/compiler_unparse.py @@ -12,14 +12,11 @@ """ import sys - -import six -import six.moves as sm - +import cStringIO from compiler.ast import Const, Name, Tuple, Div, Mul, Sub, Add def unparse(ast, single_line_functions=False): - s = sm.cStringIO.StringIO() + s = cStringIO.StringIO() UnparseCompilerAst(ast, s, single_line_functions) return s.getvalue().lstrip() diff --git a/docs/source/sphinxext/docscrape.py b/docs/source/sphinxext/docscrape.py index cf60cdf79..5e808e51d 100644 --- a/docs/source/sphinxext/docscrape.py +++ b/docs/source/sphinxext/docscrape.py @@ -1,16 +1,12 @@ """Extract reference documentation from the NumPy source tree. """ -from __future__ import print_function import inspect import textwrap import re - -import six -import six.moves as sm - import pydoc +from StringIO import StringIO from warnings import warn class Reader(object): @@ -126,7 +122,7 @@ def __getitem__(self,key): return self._parsed_data[key] def __setitem__(self,key,val): - if key not in self._parsed_data: + if not self._parsed_data.has_key(key): warn("Unknown section %s" % key) else: self._parsed_data[key] = val @@ -366,7 +362,7 @@ def _str_index(self): idx = self['index'] out = [] out += ['.. index:: %s' % idx.get('default','')] - for section, references in six.iteritems(idx): + for section, references in idx.iteritems(): if section == 'default': continue out += [' :%s: %s' % (section, ', '.join(references))] @@ -404,12 +400,12 @@ def __init__(self, func, role='func'): try: NumpyDocString.__init__(self,inspect.getdoc(func) or '') except ValueError as e: - print('*'*78) - print("ERROR: '%s' while parsing `%s`" % (e, self._f)) - print('*'*78) - #print("Docstring follows:") - #print(doclines) - #print('='*78) + print '*'*78 + print "ERROR: '%s' while parsing `%s`" % (e, self._f) + print '*'*78 + #print "Docstring follows:" + #print doclines + #print '='*78 if not self['Signature']: func, func_name = self.get_func() @@ -442,7 +438,7 @@ def __str__(self): if self._role: if self._role not in roles: - print("Warning: invalid role %s" % self._role) + print "Warning: invalid role %s" % self._role out += '.. %s:: %s\n \n\n' % (roles.get(self._role,''), func_name) diff --git a/docs/source/sphinxext/docscrape_sphinx.py b/docs/source/sphinxext/docscrape_sphinx.py index 8821ae700..a644866fa 100644 --- a/docs/source/sphinxext/docscrape_sphinx.py +++ b/docs/source/sphinxext/docscrape_sphinx.py @@ -1,9 +1,5 @@ import re, textwrap - -import six - -from .docscrape import NumpyDocString, FunctionDoc, ClassDoc - +from docscrape import NumpyDocString, FunctionDoc, ClassDoc class SphinxDocString(NumpyDocString): # string conversion routines @@ -70,7 +66,7 @@ def _str_index(self): return out out += ['.. index:: %s' % idx.get('default','')] - for section, references in six.iteritems(idx): + for section, references in idx.iteritems(): if section == 'default': continue elif section == 'refguide': diff --git a/docs/source/sphinxext/numpydoc.py b/docs/source/sphinxext/numpydoc.py index 7bc0148f8..6216a7af2 100644 --- a/docs/source/sphinxext/numpydoc.py +++ b/docs/source/sphinxext/numpydoc.py @@ -1,14 +1,7 @@ -from __future__ import print_function - import os, re, pydoc - -import six -import six.moves as sm - -from .docscrape_sphinx import SphinxDocString, SphinxClassDoc, SphinxFunctionDoc +from docscrape_sphinx import SphinxDocString, SphinxClassDoc, SphinxFunctionDoc import inspect - def mangle_docstrings(app, what, name, obj, options, lines, reference_offset=[0]): if what == 'module': @@ -33,7 +26,7 @@ def mangle_docstrings(app, what, name, obj, options, lines, try: references.append(int(l[len('.. ['):l.index(']')])) except ValueError: - print("WARNING: invalid reference in %s docstring" % name) + print "WARNING: invalid reference in %s docstring" % name # Start renaming from the biggest number, otherwise we may # overwrite references. @@ -88,7 +81,7 @@ def initialize(app): fn = app.config.numpydoc_phantom_import_file if (fn and os.path.isfile(fn)): - print("[numpydoc] Phantom importing modules from", fn, "...") + print "[numpydoc] Phantom importing modules from", fn, "..." import_phantom_module(fn) def setup(app): @@ -265,7 +258,7 @@ def _import_by_name(name): name_parts = name.split('.') last_j = 0 modname = None - for j in reversed(sm.xrange(1, len(name_parts)+1)): + for j in reversed(range(1, len(name_parts)+1)): last_j = j modname = '.'.join(name_parts[:j]) try: @@ -322,7 +315,7 @@ def monkeypatch_sphinx_ext_autodoc(): if sphinx.ext.autodoc.format_signature is our_format_signature: return - print("[numpydoc] Monkeypatching sphinx.ext.autodoc ...") + print "[numpydoc] Monkeypatching sphinx.ext.autodoc ..." _original_format_signature = sphinx.ext.autodoc.format_signature sphinx.ext.autodoc.format_signature = our_format_signature @@ -438,7 +431,6 @@ def base_cmp(a, b): doc = "%s%s\n\n%s" % (funcname, argspec, doc) obj = lambda: 0 obj.__argspec_is_invalid_ = True - obj.func_name = funcname obj.__name__ = name obj.__doc__ = doc diff --git a/docs/source/sphinxext/traitsdoc.py b/docs/source/sphinxext/traitsdoc.py index 2fe9e7949..01c6dacf4 100644 --- a/docs/source/sphinxext/traitsdoc.py +++ b/docs/source/sphinxext/traitsdoc.py @@ -1,14 +1,11 @@ -from __future__ import print_function - import inspect import os import pydoc -from . import docscrape -from .docscrape_sphinx import SphinxClassDoc, SphinxFunctionDoc -from . import numpydoc -from . import comment_eater - +import docscrape +from docscrape_sphinx import SphinxClassDoc, SphinxFunctionDoc +import numpydoc +import comment_eater class SphinxTraitsDoc(SphinxClassDoc): def __init__(self, cls, modulename='', func_doc=SphinxFunctionDoc): @@ -129,7 +126,7 @@ def initialize(app): fn = app.config.numpydoc_phantom_import_file if (fn and os.path.isfile(fn)): - print("[numpydoc] Phantom importing modules from", fn, "...") + print "[numpydoc] Phantom importing modules from", fn, "..." numpydoc.import_phantom_module(fn) def setup(app):