Merge lp:~corey.bryant/ubuntu/vivid/python-pint/0.6 into lp:ubuntu/vivid/python-pint

Proposed by Corey Bryant
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~corey.bryant/ubuntu/vivid/python-pint/0.6
Merge into: lp:ubuntu/vivid/python-pint
Diff against target: 4777 lines (+2647/-1094)
41 files modified
.gitignore (+0/-14)
.pc/applied-patches (+0/-2)
.pc/no-intersphinx.patch/docs/conf.py (+0/-299)
.pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask/layout.html (+0/-30)
.pc/removes-privacy-breach-in-doc.patch/docs/conf.py (+0/-299)
.travis.yml (+0/-65)
AUTHORS (+7/-1)
CHANGES (+33/-0)
CHANGES_DEV (+5/-0)
MANIFEST.in (+1/-1)
PKG-INFO (+400/-0)
Pint.egg-info/PKG-INFO (+400/-0)
Pint.egg-info/SOURCES.txt (+72/-0)
Pint.egg-info/dependency_links.txt (+1/-0)
Pint.egg-info/entry_points.txt (+3/-0)
Pint.egg-info/top_level.txt (+1/-0)
Pint.egg-info/zip-safe (+1/-0)
debian/changelog (+7/-0)
debian/control (+2/-0)
debian/watch (+2/-2)
docs/_themes/flask/layout.html (+1/-1)
docs/conf.py (+1/-1)
docs/contexts.rst (+13/-1)
docs/faq.rst (+2/-0)
docs/index.rst (+1/-1)
docs/nonmult.rst (+126/-27)
docs/serialization.rst (+3/-2)
docs/tutorial.rst (+0/-1)
pint/__init__.py (+49/-14)
pint/default_en.txt (+34/-4)
pint/quantity.py (+360/-74)
pint/testsuite/parameterized.py (+152/-0)
pint/testsuite/test_issues.py (+10/-5)
pint/testsuite/test_numpy.py (+70/-16)
pint/testsuite/test_quantity.py (+553/-10)
pint/testsuite/test_umath.py (+91/-32)
pint/testsuite/test_unit.py (+66/-24)
pint/unit.py (+170/-87)
setup.cfg (+9/-3)
setup.py (+1/-1)
tox.ini (+0/-77)
To merge this branch: bzr merge lp:~corey.bryant/ubuntu/vivid/python-pint/0.6
Reviewer Review Type Date Requested Status
Ubuntu Development Team Pending
Review via email: mp+245755@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

This already got uploaded without closing this MP: https://launchpad.net/ubuntu/+source/python-pint/0.6-0ubuntu1

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file '.gitignore'
2--- .gitignore 2014-09-05 23:26:05 +0000
3+++ .gitignore 1970-01-01 00:00:00 +0000
4@@ -1,14 +0,0 @@
5-*~
6-__pycache__
7-*egg-info*
8-*.pyc
9-.DS_Store
10-docs/_build/
11-.idea
12-build/
13-dist/
14-MANIFEST
15-.tox
16-
17-# WebDAV file system cache files
18-.DAV/
19
20=== removed file '.pc/applied-patches'
21--- .pc/applied-patches 2014-09-05 23:26:05 +0000
22+++ .pc/applied-patches 1970-01-01 00:00:00 +0000
23@@ -1,2 +0,0 @@
24-no-intersphinx.patch
25-removes-privacy-breach-in-doc.patch
26
27=== removed directory '.pc/no-intersphinx.patch'
28=== removed directory '.pc/no-intersphinx.patch/docs'
29=== removed file '.pc/no-intersphinx.patch/docs/conf.py'
30--- .pc/no-intersphinx.patch/docs/conf.py 2014-09-05 23:26:05 +0000
31+++ .pc/no-intersphinx.patch/docs/conf.py 1970-01-01 00:00:00 +0000
32@@ -1,299 +0,0 @@
33-#!/usr/bin/env python3
34-# -*- coding: utf-8 -*-
35-#
36-# pint documentation build configuration file, created by
37-# sphinx-quickstart on Thu Mar 1 13:33:14 2012.
38-#
39-# This file is execfile()d with the current directory set to its containing dir.
40-#
41-# Note that not all possible configuration values are present in this
42-# autogenerated file.
43-#
44-# All configuration values have a default; values that are commented out
45-# serve to show the default.
46-
47-import sys, os
48-import pkg_resources
49-import datetime
50-
51-# If extensions (or modules to document with autodoc) are in another directory,
52-# add these directories to sys.path here. If the directory is relative to the
53-# documentation root, use os.path.abspath to make it absolute, like shown here.
54-#sys.path.insert(0, os.path.abspath('.'))
55-
56-# -- General configuration -----------------------------------------------------
57-
58-# If your documentation needs a minimal Sphinx version, state it here.
59-#needs_sphinx = '1.0'
60-
61-# Add any Sphinx extension module names here, as strings. They can be extensions
62-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
63-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax']
64-
65-# Add any paths that contain templates here, relative to this directory.
66-templates_path = ['_templates']
67-
68-# The suffix of source filenames.
69-source_suffix = '.rst'
70-
71-# The encoding of source files.
72-#source_encoding = 'utf-8-sig'
73-
74-# The master toctree document.
75-master_doc = 'index'
76-
77-# General information about the project.
78-project = 'pint'
79-author = 'Hernan E. Grecco'
80-
81-# The version info for the project you're documenting, acts as replacement for
82-# |version| and |release|, also used in various other places throughout the
83-# built documents.
84-
85-version = pkg_resources.get_distribution(project).version
86-release = version
87-this_year = datetime.date.today().year
88-copyright = '%s, %s' % (this_year, author)
89-
90-# The language for content autogenerated by Sphinx. Refer to documentation
91-# for a list of supported languages.
92-#language = None
93-
94-# There are two options for replacing |today|: either, you set today to some
95-# non-false value, then it is used:
96-#today = ''
97-# Else, today_fmt is used as the format for a strftime call.
98-#today_fmt = '%B %d, %Y'
99-
100-# List of patterns, relative to source directory, that match files and
101-# directories to ignore when looking for source files.
102-exclude_patterns = ['_build']
103-
104-# The reST default role (used for this markup: `text`) to use for all documents.
105-#default_role = None
106-
107-# If true, '()' will be appended to :func: etc. cross-reference text.
108-#add_function_parentheses = True
109-
110-# If true, the current module name will be prepended to all description
111-# unit titles (such as .. function::).
112-#add_module_names = True
113-
114-# If true, sectionauthor and moduleauthor directives will be shown in the
115-# output. They are ignored by default.
116-#show_authors = False
117-
118-# The name of the Pygments (syntax highlighting) style to use.
119-pygments_style = 'sphinx'
120-
121-# A list of ignored prefixes for module index sorting.
122-#modindex_common_prefix = []
123-
124-
125-# -- Options for HTML output ---------------------------------------------------
126-
127-# The theme to use for HTML and HTML Help pages. See the documentation for
128-# a list of builtin themes.
129-#html_theme = 'default'
130-html_theme = 'flask'
131-
132-# Theme options are theme-specific and customize the look and feel of a theme
133-# further. For a list of options available for each theme, see the
134-# documentation.
135-#html_theme_options = {}
136-
137-# Add any paths that contain custom themes here, relative to this directory.
138-#html_theme_path = []
139-html_theme_path = ['_themes']
140-
141-# The name for this set of Sphinx documents. If None, it defaults to
142-# "<project> v<release> documentation".
143-#html_title = None
144-
145-# A shorter title for the navigation bar. Default is the same as html_title.
146-#html_short_title = None
147-
148-# The name of an image file (relative to this directory) to place at the top
149-# of the sidebar.
150-#html_logo = None
151-
152-# The name of an image file (within the static path) to use as favicon of the
153-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
154-# pixels large.
155-#html_favicon = None
156-
157-# Add any paths that contain custom static files (such as style sheets) here,
158-# relative to this directory. They are copied after the builtin static files,
159-# so a file named "default.css" will overwrite the builtin "default.css".
160-html_static_path = ['_static']
161-
162-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
163-# using the given strftime format.
164-#html_last_updated_fmt = '%b %d, %Y'
165-
166-# If true, SmartyPants will be used to convert quotes and dashes to
167-# typographically correct entities.
168-#html_use_smartypants = True
169-
170-# Custom sidebar templates, maps document names to template names.
171-#html_sidebars = {}
172-html_sidebars = {
173- 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
174- '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
175- 'sourcelink.html', 'searchbox.html']
176-}
177-
178-# Additional templates that should be rendered to pages, maps page names to
179-# template names.
180-#html_additional_pages = {}
181-
182-# If false, no module index is generated.
183-#html_domain_indices = True
184-
185-# If false, no index is generated.
186-#html_use_index = True
187-
188-# If true, the index is split into individual pages for each letter.
189-#html_split_index = False
190-
191-# If true, links to the reST sources are added to the pages.
192-#html_show_sourcelink = True
193-
194-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
195-#html_show_sphinx = True
196-
197-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
198-#html_show_copyright = True
199-
200-# If true, an OpenSearch description file will be output, and all pages will
201-# contain a <link> tag referring to it. The value of this option must be the
202-# base URL from which the finished HTML is served.
203-#html_use_opensearch = ''
204-
205-# This is the file name suffix for HTML files (e.g. ".xhtml").
206-#html_file_suffix = None
207-
208-# Output file base name for HTML help builder.
209-htmlhelp_basename = 'pintdoc'
210-
211-
212-# -- Options for LaTeX output --------------------------------------------------
213-
214-latex_elements = {
215-# The paper size ('letterpaper' or 'a4paper').
216-#'papersize': 'letterpaper',
217-
218-# The font size ('10pt', '11pt' or '12pt').
219-#'pointsize': '10pt',
220-
221-# Additional stuff for the LaTeX preamble.
222-#'preamble': '',
223-}
224-
225-# Grouping the document tree into LaTeX files. List of tuples
226-# (source start file, target name, title, author, documentclass [howto/manual]).
227-latex_documents = [
228- ('index', 'pint.tex', 'pint Documentation',
229- 'Hernan E. Grecco', 'manual'),
230-]
231-
232-# The name of an image file (relative to this directory) to place at the top of
233-# the title page.
234-#latex_logo = None
235-
236-# For "manual" documents, if this is true, then toplevel headings are parts,
237-# not chapters.
238-#latex_use_parts = False
239-
240-# If true, show page references after internal links.
241-#latex_show_pagerefs = False
242-
243-# If true, show URL addresses after external links.
244-#latex_show_urls = False
245-
246-# Documents to append as an appendix to all manuals.
247-#latex_appendices = []
248-
249-# If false, no module index is generated.
250-#latex_domain_indices = True
251-
252-
253-# -- Options for manual page output --------------------------------------------
254-
255-# One entry per manual page. List of tuples
256-# (source start file, name, description, authors, manual section).
257-man_pages = [
258- ('index', 'pint', 'pint Documentation',
259- ['Hernan E. Grecco'], 1)
260-]
261-
262-# If true, show URL addresses after external links.
263-#man_show_urls = False
264-
265-
266-# -- Options for Texinfo output ------------------------------------------------
267-
268-# Grouping the document tree into Texinfo files. List of tuples
269-# (source start file, target name, title, author,
270-# dir menu entry, description, category)
271-texinfo_documents = [
272- ('index', 'pint', 'pint Documentation',
273- 'Hernan E. Grecco', 'pint', 'One line description of project.',
274- 'Miscellaneous'),
275-]
276-
277-# Documents to append as an appendix to all manuals.
278-#texinfo_appendices = []
279-
280-# If false, no module index is generated.
281-#texinfo_domain_indices = True
282-
283-# How to display URL addresses: 'footnote', 'no', or 'inline'.
284-#texinfo_show_urls = 'footnote'
285-
286-
287-# -- Options for Epub output ---------------------------------------------------
288-
289-# Bibliographic Dublin Core info.
290-epub_title = project
291-epub_author = author
292-epub_publisher = author
293-epub_copyright = copyright
294-
295-# The language of the text. It defaults to the language option
296-# or en if the language is not set.
297-#epub_language = ''
298-
299-# The scheme of the identifier. Typical schemes are ISBN or URL.
300-#epub_scheme = ''
301-
302-# The unique identifier of the text. This can be a ISBN number
303-# or the project homepage.
304-#epub_identifier = ''
305-
306-# A unique identification for the text.
307-#epub_uid = ''
308-
309-# A tuple containing the cover image and cover page html template filenames.
310-#epub_cover = ()
311-
312-# HTML files that should be inserted before the pages created by sphinx.
313-# The format is a list of tuples containing the path and title.
314-#epub_pre_files = []
315-
316-# HTML files shat should be inserted after the pages created by sphinx.
317-# The format is a list of tuples containing the path and title.
318-#epub_post_files = []
319-
320-# A list of files that should not be packed into the epub file.
321-#epub_exclude_files = []
322-
323-# The depth of the table of contents in toc.ncx.
324-#epub_tocdepth = 3
325-
326-# Allow duplicate toc entries.
327-#epub_tocdup = True
328-
329-
330-# Example configuration for intersphinx: refer to the Python standard library.
331-intersphinx_mapping = {'http://docs.python.org/': None}
332
333=== removed directory '.pc/removes-privacy-breach-in-doc.patch'
334=== removed directory '.pc/removes-privacy-breach-in-doc.patch/docs'
335=== removed directory '.pc/removes-privacy-breach-in-doc.patch/docs/_themes'
336=== removed directory '.pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask'
337=== removed file '.pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask/layout.html'
338--- .pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask/layout.html 2014-09-05 23:26:05 +0000
339+++ .pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask/layout.html 1970-01-01 00:00:00 +0000
340@@ -1,30 +0,0 @@
341-{%- extends "basic/layout.html" %}
342-{%- block extrahead %}
343- {{ super() }}
344- {% if theme_touch_icon %}
345- <link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
346- {% endif %}
347- <link media="only screen and (max-device-width: 480px)" href="{{
348- pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
349-{% endblock %}
350-{%- block relbar2 %}
351- {% if theme_github_fork %}
352- <a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
353- src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
354- {% endif %}
355-{% endblock %}
356-{% block header %}
357- {{ super() }}
358- {% if pagename == 'index' %}
359- <div>
360- {% endif %}
361-{% endblock %}
362-{%- block footer %}
363- <div class="footer">
364- &copy; Copyright {{ copyright }}. Pint {{ version }}.
365- Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
366- </div>
367- {% if pagename == 'index' %}
368- </div>
369- {% endif %}
370-{%- endblock %}
371
372=== removed file '.pc/removes-privacy-breach-in-doc.patch/docs/conf.py'
373--- .pc/removes-privacy-breach-in-doc.patch/docs/conf.py 2014-09-05 23:26:05 +0000
374+++ .pc/removes-privacy-breach-in-doc.patch/docs/conf.py 1970-01-01 00:00:00 +0000
375@@ -1,299 +0,0 @@
376-#!/usr/bin/env python3
377-# -*- coding: utf-8 -*-
378-#
379-# pint documentation build configuration file, created by
380-# sphinx-quickstart on Thu Mar 1 13:33:14 2012.
381-#
382-# This file is execfile()d with the current directory set to its containing dir.
383-#
384-# Note that not all possible configuration values are present in this
385-# autogenerated file.
386-#
387-# All configuration values have a default; values that are commented out
388-# serve to show the default.
389-
390-import sys, os
391-import pkg_resources
392-import datetime
393-
394-# If extensions (or modules to document with autodoc) are in another directory,
395-# add these directories to sys.path here. If the directory is relative to the
396-# documentation root, use os.path.abspath to make it absolute, like shown here.
397-#sys.path.insert(0, os.path.abspath('.'))
398-
399-# -- General configuration -----------------------------------------------------
400-
401-# If your documentation needs a minimal Sphinx version, state it here.
402-#needs_sphinx = '1.0'
403-
404-# Add any Sphinx extension module names here, as strings. They can be extensions
405-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
406-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax']
407-
408-# Add any paths that contain templates here, relative to this directory.
409-templates_path = ['_templates']
410-
411-# The suffix of source filenames.
412-source_suffix = '.rst'
413-
414-# The encoding of source files.
415-#source_encoding = 'utf-8-sig'
416-
417-# The master toctree document.
418-master_doc = 'index'
419-
420-# General information about the project.
421-project = 'pint'
422-author = 'Hernan E. Grecco'
423-
424-# The version info for the project you're documenting, acts as replacement for
425-# |version| and |release|, also used in various other places throughout the
426-# built documents.
427-
428-version = pkg_resources.get_distribution(project).version
429-release = version
430-this_year = datetime.date.today().year
431-copyright = '%s, %s' % (this_year, author)
432-
433-# The language for content autogenerated by Sphinx. Refer to documentation
434-# for a list of supported languages.
435-#language = None
436-
437-# There are two options for replacing |today|: either, you set today to some
438-# non-false value, then it is used:
439-#today = ''
440-# Else, today_fmt is used as the format for a strftime call.
441-#today_fmt = '%B %d, %Y'
442-
443-# List of patterns, relative to source directory, that match files and
444-# directories to ignore when looking for source files.
445-exclude_patterns = ['_build']
446-
447-# The reST default role (used for this markup: `text`) to use for all documents.
448-#default_role = None
449-
450-# If true, '()' will be appended to :func: etc. cross-reference text.
451-#add_function_parentheses = True
452-
453-# If true, the current module name will be prepended to all description
454-# unit titles (such as .. function::).
455-#add_module_names = True
456-
457-# If true, sectionauthor and moduleauthor directives will be shown in the
458-# output. They are ignored by default.
459-#show_authors = False
460-
461-# The name of the Pygments (syntax highlighting) style to use.
462-pygments_style = 'sphinx'
463-
464-# A list of ignored prefixes for module index sorting.
465-#modindex_common_prefix = []
466-
467-
468-# -- Options for HTML output ---------------------------------------------------
469-
470-# The theme to use for HTML and HTML Help pages. See the documentation for
471-# a list of builtin themes.
472-#html_theme = 'default'
473-html_theme = 'flask'
474-
475-# Theme options are theme-specific and customize the look and feel of a theme
476-# further. For a list of options available for each theme, see the
477-# documentation.
478-#html_theme_options = {}
479-
480-# Add any paths that contain custom themes here, relative to this directory.
481-#html_theme_path = []
482-html_theme_path = ['_themes']
483-
484-# The name for this set of Sphinx documents. If None, it defaults to
485-# "<project> v<release> documentation".
486-#html_title = None
487-
488-# A shorter title for the navigation bar. Default is the same as html_title.
489-#html_short_title = None
490-
491-# The name of an image file (relative to this directory) to place at the top
492-# of the sidebar.
493-#html_logo = None
494-
495-# The name of an image file (within the static path) to use as favicon of the
496-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
497-# pixels large.
498-#html_favicon = None
499-
500-# Add any paths that contain custom static files (such as style sheets) here,
501-# relative to this directory. They are copied after the builtin static files,
502-# so a file named "default.css" will overwrite the builtin "default.css".
503-html_static_path = ['_static']
504-
505-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
506-# using the given strftime format.
507-#html_last_updated_fmt = '%b %d, %Y'
508-
509-# If true, SmartyPants will be used to convert quotes and dashes to
510-# typographically correct entities.
511-#html_use_smartypants = True
512-
513-# Custom sidebar templates, maps document names to template names.
514-#html_sidebars = {}
515-html_sidebars = {
516- 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
517- '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
518- 'sourcelink.html', 'searchbox.html']
519-}
520-
521-# Additional templates that should be rendered to pages, maps page names to
522-# template names.
523-#html_additional_pages = {}
524-
525-# If false, no module index is generated.
526-#html_domain_indices = True
527-
528-# If false, no index is generated.
529-#html_use_index = True
530-
531-# If true, the index is split into individual pages for each letter.
532-#html_split_index = False
533-
534-# If true, links to the reST sources are added to the pages.
535-#html_show_sourcelink = True
536-
537-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
538-#html_show_sphinx = True
539-
540-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
541-#html_show_copyright = True
542-
543-# If true, an OpenSearch description file will be output, and all pages will
544-# contain a <link> tag referring to it. The value of this option must be the
545-# base URL from which the finished HTML is served.
546-#html_use_opensearch = ''
547-
548-# This is the file name suffix for HTML files (e.g. ".xhtml").
549-#html_file_suffix = None
550-
551-# Output file base name for HTML help builder.
552-htmlhelp_basename = 'pintdoc'
553-
554-
555-# -- Options for LaTeX output --------------------------------------------------
556-
557-latex_elements = {
558-# The paper size ('letterpaper' or 'a4paper').
559-#'papersize': 'letterpaper',
560-
561-# The font size ('10pt', '11pt' or '12pt').
562-#'pointsize': '10pt',
563-
564-# Additional stuff for the LaTeX preamble.
565-#'preamble': '',
566-}
567-
568-# Grouping the document tree into LaTeX files. List of tuples
569-# (source start file, target name, title, author, documentclass [howto/manual]).
570-latex_documents = [
571- ('index', 'pint.tex', 'pint Documentation',
572- 'Hernan E. Grecco', 'manual'),
573-]
574-
575-# The name of an image file (relative to this directory) to place at the top of
576-# the title page.
577-#latex_logo = None
578-
579-# For "manual" documents, if this is true, then toplevel headings are parts,
580-# not chapters.
581-#latex_use_parts = False
582-
583-# If true, show page references after internal links.
584-#latex_show_pagerefs = False
585-
586-# If true, show URL addresses after external links.
587-#latex_show_urls = False
588-
589-# Documents to append as an appendix to all manuals.
590-#latex_appendices = []
591-
592-# If false, no module index is generated.
593-#latex_domain_indices = True
594-
595-
596-# -- Options for manual page output --------------------------------------------
597-
598-# One entry per manual page. List of tuples
599-# (source start file, name, description, authors, manual section).
600-man_pages = [
601- ('index', 'pint', 'pint Documentation',
602- ['Hernan E. Grecco'], 1)
603-]
604-
605-# If true, show URL addresses after external links.
606-#man_show_urls = False
607-
608-
609-# -- Options for Texinfo output ------------------------------------------------
610-
611-# Grouping the document tree into Texinfo files. List of tuples
612-# (source start file, target name, title, author,
613-# dir menu entry, description, category)
614-texinfo_documents = [
615- ('index', 'pint', 'pint Documentation',
616- 'Hernan E. Grecco', 'pint', 'One line description of project.',
617- 'Miscellaneous'),
618-]
619-
620-# Documents to append as an appendix to all manuals.
621-#texinfo_appendices = []
622-
623-# If false, no module index is generated.
624-#texinfo_domain_indices = True
625-
626-# How to display URL addresses: 'footnote', 'no', or 'inline'.
627-#texinfo_show_urls = 'footnote'
628-
629-
630-# -- Options for Epub output ---------------------------------------------------
631-
632-# Bibliographic Dublin Core info.
633-epub_title = project
634-epub_author = author
635-epub_publisher = author
636-epub_copyright = copyright
637-
638-# The language of the text. It defaults to the language option
639-# or en if the language is not set.
640-#epub_language = ''
641-
642-# The scheme of the identifier. Typical schemes are ISBN or URL.
643-#epub_scheme = ''
644-
645-# The unique identifier of the text. This can be a ISBN number
646-# or the project homepage.
647-#epub_identifier = ''
648-
649-# A unique identification for the text.
650-#epub_uid = ''
651-
652-# A tuple containing the cover image and cover page html template filenames.
653-#epub_cover = ()
654-
655-# HTML files that should be inserted before the pages created by sphinx.
656-# The format is a list of tuples containing the path and title.
657-#epub_pre_files = []
658-
659-# HTML files shat should be inserted after the pages created by sphinx.
660-# The format is a list of tuples containing the path and title.
661-#epub_post_files = []
662-
663-# A list of files that should not be packed into the epub file.
664-#epub_exclude_files = []
665-
666-# The depth of the table of contents in toc.ncx.
667-#epub_tocdepth = 3
668-
669-# Allow duplicate toc entries.
670-#epub_tocdup = True
671-
672-
673-# Example configuration for intersphinx: refer to the Python standard library.
674-intersphinx_mapping = {'http://docs.python.org/': None}
675
676=== removed file '.travis.yml'
677--- .travis.yml 2014-09-05 23:26:05 +0000
678+++ .travis.yml 1970-01-01 00:00:00 +0000
679@@ -1,65 +0,0 @@
680-language: python
681-
682-python:
683- - "2.6"
684- - "2.7"
685- - "3.2"
686- - "3.3"
687- - "3.4"
688-
689-env:
690- - UNCERTAINTIES="N" NUMPY_VERSION=0
691- - UNCERTAINTIES="N" NUMPY_VERSION=1.6.2
692- - UNCERTAINTIES="N" NUMPY_VERSION=1.7.1
693- - UNCERTAINTIES="N" NUMPY_VERSION=1.8.1
694- - UNCERTAINTIES="Y" NUMPY_VERSION=0
695- - UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2
696- - UNCERTAINTIES="Y" NUMPY_VERSION=1.7.1
697- - UNCERTAINTIES="Y" NUMPY_VERSION=1.8.1
698-
699-branches:
700- only:
701- - master
702- - develop
703-
704-before_install:
705- - wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
706- - chmod +x miniconda.sh
707- - ./miniconda.sh -b
708- - export PATH=/home/travis/miniconda/bin:$PATH
709- - conda update --yes conda
710-
711- # The next couple lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda
712- - sudo rm -rf /dev/shm
713- - sudo ln -s /run/shm /dev/shm
714-
715-install:
716- - conda create -c mwcraig --yes -n env_name python=$TRAVIS_PYTHON_VERSION pip
717- - source activate env_name
718- - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then pip install unittest2; fi
719- - if [ $UNCERTAINTIES == 'Y' ]; then pip install uncertainties; fi
720- - if [ $NUMPY_VERSION != '0' ]; then conda install -c mwcraig --yes numpy==$NUMPY_VERSION; fi
721- - pip install coverage coveralls
722-
723-script:
724- - coverage run -p --source=pint --omit="*test*","*compat*" setup.py test
725- - coverage combine
726- - coverage report -m
727-
728-after_script:
729- - coveralls --verbose
730-
731-matrix:
732- exclude:
733- - python: "3.3"
734- env: UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2
735- - python: "3.4"
736- env: UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2
737- - python: "3.4"
738- env: UNCERTAINTIES="Y" NUMPY_VERSION=1.7.1
739- - python: "3.3"
740- env: UNCERTAINTIES="N" NUMPY_VERSION=1.6.2
741- - python: "3.4"
742- env: UNCERTAINTIES="N" NUMPY_VERSION=1.6.2
743- - python: "3.4"
744- env: UNCERTAINTIES="N" NUMPY_VERSION=1.7.1
745
746=== modified file 'AUTHORS'
747--- AUTHORS 2014-09-05 23:26:05 +0000
748+++ AUTHORS 2015-01-07 15:51:38 +0000
749@@ -5,9 +5,9 @@
750 * Alexander Böhn <fish2000@gmail.com>
751 * Brend Wanders <b.wanders@utwente.nl>
752 * choloepus
753-* coutinho <coutinho@esrf.fr>
754 * Daniel Sokolowski <daniel.sokolowski@danols.com>
755 * Dave Brooks <dave@bcs.co.nz>
756+* David Linke
757 * Eduard Bopp <eduard.bopp@aepsil0n.de>
758 * Felix Hummel <felix@felixhummel.de>
759 * Giel van Schijndel <me@mortis.eu>
760@@ -15,11 +15,17 @@
761 * Jim Turner <jturner314@gmail.com>
762 * Joel B. Mohler <joel@kiwistrawberry.us>
763 * John David Reaver <jdreaver@adlerhorst.com>
764+* Jonas Olson <jolson@kth.se>
765 * Kenneth D. Mankoff <mankoff@gmail.com>
766 * Luke Campbell <luke.s.campbell@gmail.com>
767+* Matthieu Dartiailh <marul@laposte.net>
768 * Nate Bogdanowicz <natezb@gmail.com>
769 * Peter Grayson <jpgrayson@gmail.com>
770 * Richard Barnes <rbarnes@umn.edu>
771+* Ryan Kingsbury <RyanSKingsbury@alumni.unc.edu>
772 * Sundar Raman <cybertoast@gmail.com>
773 * Tiago Coutinho <coutinho@esrf.fr>
774 * Tom Ritchford <tom@swirly.com>
775+* Virgil Dupras <virgil.dupras@savoirfairelinux.com>
776+
777+(If you think that your name belongs here, please let the maintainer know)
778
779=== modified file 'CHANGES'
780--- CHANGES 2014-09-05 23:26:05 +0000
781+++ CHANGES 2015-01-07 15:51:38 +0000
782@@ -2,6 +2,39 @@
783 ==============
784
785
786+0.6 (2014-11-07)
787+----------------
788+
789+- Fix operations with measurments and user defined units.
790+ (Issue #204)
791+- Faster conversions through caching and other performance improvements.
792+ (Issue #193, thanks MatthieuDartiailh)
793+- Better error messages on Quantity.__setitem__.
794+ (Issue #191)
795+- Fixed abbreviation of fluid_ounce.
796+ (Issue #187, thanks hsoft)
797+- Defined Angstrom symbol.
798+ (Issue #181, thanks JonasOlson)
799+- Removed fetching version from git repo as it triggers XCode installation on OSX.
800+ (Issue #178, thanks deanishe)
801+- Improved context documentation.
802+ (Issue #176 and 179, thanks rsking84)
803+- Added Chemistry context.
804+ (Issue #179, thanks rsking84)
805+- Fix help(UnitRegisty)
806+ (Issue #168)
807+- Optimized "get_dimensionality" and "get_base_name".
808+ (Issue #166 and #167, thanks jbmohler)
809+- Renamed ureg.parse_units parameter "to_delta" to "as_delta" to make clear.
810+ that no conversion happens. Accordingly, the parameter/property
811+ "default_to_delta" of UnitRegistry was renamed to "default_as_delta".
812+ (Issue #158, thanks dalit)
813+- Fixed problem when adding two uncertainties.
814+ (thanks dalito)
815+- Full support for Offset units (e.g. temperature)
816+ (Issue #88, #143, #147 and #161, thanks dalito)
817+
818+
819 0.5.2 (2014-07-31)
820 ------------------
821
822
823=== added file 'CHANGES_DEV'
824--- CHANGES_DEV 1970-01-01 00:00:00 +0000
825+++ CHANGES_DEV 2015-01-07 15:51:38 +0000
826@@ -0,0 +1,5 @@
827+
828+
829+
830+0.7 (unreleased)
831+----------------
832
833=== modified file 'MANIFEST.in'
834--- MANIFEST.in 2014-09-05 23:26:05 +0000
835+++ MANIFEST.in 2015-01-07 15:51:38 +0000
836@@ -1,4 +1,4 @@
837-include README AUTHORS CHANGES LICENSE
838+include README AUTHORS CHANGES LICENSE CHANGES_DEV
839 recursive-include pint *
840 recursive-include docs *
841 recursive-include bench *
842
843=== added file 'PKG-INFO'
844--- PKG-INFO 1970-01-01 00:00:00 +0000
845+++ PKG-INFO 2015-01-07 15:51:38 +0000
846@@ -0,0 +1,400 @@
847+Metadata-Version: 1.1
848+Name: Pint
849+Version: 0.6
850+Summary: Physical quantities module
851+Home-page: https://github.com/hgrecco/pint
852+Author: Hernan E. Grecco
853+Author-email: hernan.grecco@gmail.com
854+License: BSD
855+Description: Pint: a Python units library
856+ ============================
857+
858+ Pint is Python module/package to define, operate and manipulate physical
859+ quantities: the product of a numerical value and a unit of measurement.
860+ It allows arithmetic operations between them and conversions from and
861+ to different units.
862+
863+ It is distributed with a comprehensive list of physical units, prefixes
864+ and constants. Due to its modular design, you to extend (or even rewrite!)
865+ the complete list without changing the source code.
866+
867+ It has a complete test coverage. It runs in Python 2.7 and 3.X
868+ with no other dependency. It licensed under BSD.
869+
870+
871+ Design principles
872+ -----------------
873+
874+ Although there are already a few very good Python packages to handle physical
875+ quantities, no one was really fitting my needs. Like most developers, I programed
876+ Pint to scratch my own itches.
877+
878+ - Unit parsing: prefixed and pluralized forms of units are recognized without
879+ explicitly defining them. In other words: as the prefix *kilo* and the unit *meter*
880+ are defined, Pint understands *kilometers*. This results in a much shorter and
881+ maintainable unit definition list as compared to other packages.
882+
883+ - Standalone unit definitions: units definitions are loaded from simple and
884+ easy to edit text file. Adding and changing units and their definitions does
885+ not involve changing the code.
886+
887+ - Advanced string formatting: a quantity can be formatted into string using
888+ PEP 3101 syntax. Extended conversion flags are given to provide latex and pretty
889+ formatting.
890+
891+ - Small codebase: small and easy to maintain with a flat hierarchy.
892+
893+ - Dependency free: it depends only on Python and its standard library.
894+
895+ - Python 2 and 3: A single codebase that runs unchanged in Python 2.7+ and Python 3.0+.
896+
897+ - Advanced NumPy support: While NumPy is not a requirement for Pint,
898+ when available ndarray methods and ufuncs can be used in Quantity objects.
899+
900+
901+ Pint is written and maintained by Hernan E. Grecco <hernan.grecco@gmail.com>.
902+
903+ Other contributors, listed alphabetically, are:
904+
905+ * Alexander Böhn <fish2000@gmail.com>
906+ * Brend Wanders <b.wanders@utwente.nl>
907+ * choloepus
908+ * Daniel Sokolowski <daniel.sokolowski@danols.com>
909+ * Dave Brooks <dave@bcs.co.nz>
910+ * David Linke
911+ * Eduard Bopp <eduard.bopp@aepsil0n.de>
912+ * Felix Hummel <felix@felixhummel.de>
913+ * Giel van Schijndel <me@mortis.eu>
914+ * James Rowe <jnrowe@gmail.com>
915+ * Jim Turner <jturner314@gmail.com>
916+ * Joel B. Mohler <joel@kiwistrawberry.us>
917+ * John David Reaver <jdreaver@adlerhorst.com>
918+ * Jonas Olson <jolson@kth.se>
919+ * Kenneth D. Mankoff <mankoff@gmail.com>
920+ * Luke Campbell <luke.s.campbell@gmail.com>
921+ * Matthieu Dartiailh <marul@laposte.net>
922+ * Nate Bogdanowicz <natezb@gmail.com>
923+ * Peter Grayson <jpgrayson@gmail.com>
924+ * Richard Barnes <rbarnes@umn.edu>
925+ * Ryan Kingsbury <RyanSKingsbury@alumni.unc.edu>
926+ * Sundar Raman <cybertoast@gmail.com>
927+ * Tiago Coutinho <coutinho@esrf.fr>
928+ * Tom Ritchford <tom@swirly.com>
929+ * Virgil Dupras <virgil.dupras@savoirfairelinux.com>
930+
931+ (If you think that your name belongs here, please let the maintainer know)
932+
933+
934+ Pint Changelog
935+ ==============
936+
937+
938+ 0.6 (2014-11-07)
939+ ----------------
940+
941+ - Fix operations with measurments and user defined units.
942+ (Issue #204)
943+ - Faster conversions through caching and other performance improvements.
944+ (Issue #193, thanks MatthieuDartiailh)
945+ - Better error messages on Quantity.__setitem__.
946+ (Issue #191)
947+ - Fixed abbreviation of fluid_ounce.
948+ (Issue #187, thanks hsoft)
949+ - Defined Angstrom symbol.
950+ (Issue #181, thanks JonasOlson)
951+ - Removed fetching version from git repo as it triggers XCode installation on OSX.
952+ (Issue #178, thanks deanishe)
953+ - Improved context documentation.
954+ (Issue #176 and 179, thanks rsking84)
955+ - Added Chemistry context.
956+ (Issue #179, thanks rsking84)
957+ - Fix help(UnitRegisty)
958+ (Issue #168)
959+ - Optimized "get_dimensionality" and "get_base_name".
960+ (Issue #166 and #167, thanks jbmohler)
961+ - Renamed ureg.parse_units parameter "to_delta" to "as_delta" to make clear.
962+ that no conversion happens. Accordingly, the parameter/property
963+ "default_to_delta" of UnitRegistry was renamed to "default_as_delta".
964+ (Issue #158, thanks dalit)
965+ - Fixed problem when adding two uncertainties.
966+ (thanks dalito)
967+ - Full support for Offset units (e.g. temperature)
968+ (Issue #88, #143, #147 and #161, thanks dalito)
969+
970+
971+ 0.5.2 (2014-07-31)
972+ ------------------
973+
974+ - Changed travis config to use miniconda for faster testing.
975+ - Added wheel configuration to setup.cfg.
976+ - Ensure resource streams are closed after reading.
977+ - Require setuptools.
978+ (Issue #169)
979+ - Implemented real, imag and T Quantity properties.
980+ (Issue #171)
981+ - Implemented __int__ and __long__ for Quantity
982+ (Issue #170)
983+ - Fixed SI prefix error on ureg.convert.
984+ (Issue #156, thanks jdreaver)
985+ - Fixed parsing of multiparemeter contexts.
986+ (Issue #174)
987+
988+
989+ 0.5.1 (2014-06-03)
990+ ------------------
991+
992+ - Implemented a standard way to change the registry used in unpickling operations.
993+ (Issue #148)
994+ - Fix bug where conversion would fail due to caching.
995+ (Issue #140, thanks jdreaver)
996+ - Allow assigning Not a Number to a quantity array.
997+ (Issue #127)
998+ - Decoupled Quantity in place and not in place unit conversion methods.
999+ - Return None in functions that modify quantities in place.
1000+ - Improved testing infrastructure to check for unwanted warnings.
1001+ - Added test function at the package level to run all tests.
1002+
1003+
1004+ 0.5 (2014-05-07)
1005+ ----------------
1006+
1007+ - Improved test suite helper functions.
1008+ - Print honors default format w/o format().
1009+ (Issue #132, thanks mankoff)
1010+ - Fixed sum() by treating number zero as a special case.
1011+ (Issue #122, thanks rec)
1012+ - Improved behaviour in ScaleConverter, OffsetConverter and Quantity.to.
1013+ (Issue #120)
1014+ - Reimplemented loading of default definitions to allow Pint in a cx_freeze or similar package.
1015+ (Issue #118, thanks jbmohler)
1016+ - Implemented parsing of pretty printed units.
1017+ (Issue #117, thanks jpgrayson)
1018+ - Fixed representation of dimensionless quantities.
1019+ (Issue #112, thanks rec)
1020+ - Raise error when invalid formatting code is given.
1021+ (Issue #111, thanks rec)
1022+ - Default registry to lazy load, raise error on redefinition
1023+ (Issue #108, thanks rec, aepsil0n)
1024+ - Added condensed format.
1025+ (Issue #107, thanks rec)
1026+ - Added UnitRegistry () operator to parse expression replacing [].
1027+ (Issue #106, thanks rec)
1028+ - Optional case insensitive unit parsing.
1029+ (Issue #105, thanks rec, jeremyfreeman, dbrnz)
1030+ - Change the Quantity mutability depending on magnitude type.
1031+ (Issue #104, thanks rec)
1032+ - Implemented API to list compatible units.
1033+ (Issue #89)
1034+ - Implemented cache of key UnitRegistry methods.
1035+ - Rewrote the Measurement class to use uncertainties.
1036+ (Issue #24)
1037+
1038+
1039+ 0.4.2 (2014-02-14)
1040+ ------------------
1041+
1042+ - Python 2.6 support
1043+ (Issue #96, thanks tiagocoutinho)
1044+ - Fixed symbol for inch.
1045+ (Issue #102, thanks cybertoast)
1046+ - Stop raising AttributeError when wrapping funcs without all of the attributes.
1047+ (Issue #100, thanks jturner314)
1048+ - Fixed warning appearing in Py2.x when comparing a Numpy Array with an empty string.
1049+ (Issue #98, thanks jturner314)
1050+ - Add links to AUR packages in docs.
1051+ (Issue #91, thanks jturner314)
1052+ - Fixed garbage collection related problem.
1053+ (Issue #92, thanks jturner314)
1054+
1055+
1056+ 0.4.1 (2014-01-12)
1057+ ------------------
1058+
1059+ - Integer Division with Arrays.
1060+ (Issue #80, thanks jdreaver)
1061+ - Improved Documentation.
1062+ (Issue #83, thanks choloepus)
1063+ - Removed 'h' alias for hour due to conflict with Planck's constant.
1064+ (Issue #82, thanks choloepus)
1065+ - Improved get_base_units for non-multiplicative units.
1066+ (Issue #85, thanks exxus)
1067+ - Refactored code for multiplication.
1068+ (Issue #84, thanks jturner314)
1069+ - Removed 'R' alias for roentgen as it collides with molar_gas_constant.
1070+ (Issue #87, thanks rsking84)
1071+ - Improved naming of temperature units and multiplication of non-multiplicative units.
1072+ (Issue #86, tahsnk exxus)
1073+
1074+
1075+
1076+ 0.4 (2013-12-17)
1077+ ----------------
1078+
1079+ - Introduced Contexts: relation between incompatible dimensions.
1080+ (Issue #65)
1081+ - Fixed get_base_units for non multiplicative units.
1082+ (Related to issue #66)
1083+ - Implemented default formatting for quantities.
1084+ - Changed comparison between Quantities containing NumPy arrays.
1085+ (Issue #75) - BACKWARDS INCOMPATIBLE CHANGE
1086+ - Fixes for NumPy 1.8 due to changes in handling binary ops.
1087+ (Issue #73)
1088+
1089+
1090+ 0.3.3 (2013-11-29)
1091+ ------------------
1092+
1093+ - ParseHelper can now parse units named like python keywords.
1094+ (Issue #69)
1095+ - Fix comparison of quantities.
1096+ (Issue #74)
1097+ - Fix Inequality operator.
1098+ (Issue #70, thanks muggenhor)
1099+ - Improved travis configuration.
1100+ (thanks muggenhor)
1101+
1102+
1103+ 0.3.2 (2013-10-22)
1104+ ------------------
1105+
1106+ - Fix get_dimensionality for non multiplicative units.
1107+ (Issue #66)
1108+ - Proper handling of @import directive inside a file read using pkg_resources.
1109+ (Issue #68)
1110+
1111+
1112+ 0.3.1 (2013-09-15)
1113+ ------------------
1114+
1115+ - fix right division on python 2.7
1116+ (Issue #58, thanks natezb)
1117+ - fix formatting of fractional exponentials between 0 and 1.
1118+ (Issue #62, thanks jdreaver)
1119+ - fix installation as egg.
1120+ (Issue #61)
1121+ - fix handling of strange values as input of Quantity.
1122+ (Issue #53)
1123+ - math operations between quantities of different registries now raise a ValueError.
1124+ (Issue #52)
1125+
1126+
1127+ 0.3 (2013-09-02)
1128+ ----------------
1129+
1130+ - support for IPython autocomplete and rich display.
1131+ (Issues #30 and #31)
1132+ - support for @import directive in definitions file.
1133+ (Issue #22)
1134+ - support for wrapping functions to make them pint-aware.
1135+ (Issue #16)
1136+ - support for comparing UnitsContainer to string.
1137+ (Issue #35)
1138+ - fix error raised while converting from a single unit to one expressed as
1139+ the relation between many.
1140+ (Issue #29)
1141+ - fix error raised when unit symbol is missing.
1142+ (Issue #41)
1143+ - fix error raised when magnitude is Decimal.
1144+ (Issue #46, thanks danielsokolowski)
1145+ - support for non-installed pint.
1146+ (Issue #42, thanks danielsokolowski)
1147+ - support for application of numpy function on non-ndarray magnitudes.
1148+ (Issue #44)
1149+ - support for math operations on dimensionless Quantities (written with units).
1150+ (Issue #45)
1151+ - fix obtaining dimensionless quantity from string.
1152+ (Issue #50)
1153+ - fix adding and comparing numbers to a dimensionless quantity (written with units).
1154+ (Issue #54)
1155+ - Support for iter in Quantity.
1156+ (Issue #55, thanks natezb)
1157+
1158+
1159+ 0.2.1 (2013-07-02)
1160+ ------------------
1161+
1162+ - fix error raised while converting from a single unit to one expressed as
1163+ the relation between many.
1164+ (Issue #29)
1165+
1166+
1167+ 0.2 (2013-05-13)
1168+ ----------------
1169+
1170+ - support for Measurement (Quantity +/- error).
1171+ - implemented buckingham pi theorem for dimensional analysis.
1172+ - support for temperature units and temperature difference units.
1173+ - parser can infers if the user mean temperature or temperature difference.
1174+ - support for derived dimensions (e.g. [speed] = [length] / [time]).
1175+ - refactored the code into multiple files.
1176+ - refactored code to isolate definitions and converters.
1177+ - refactored formatter out of UnitParser class.
1178+ - added tox and travis config files for CI.
1179+ - comprehensive NumPy testing including almost all ufuncs.
1180+ - full NumPy support (features is not longer experimental).
1181+ - fixed bug preventing from having independent registries.
1182+ (Issue #10, thanks bwanders)
1183+ - forces real division as default for Quantities.
1184+ (Issue #7, thanks dbrnz)
1185+ - improved default unit definition file.
1186+ (Issue #13, thanks r-barnes)
1187+ - smarter parser supporting spaces as multiplications and other nice features.
1188+ (Issue #13, thanks r-barnes)
1189+ - moved testsuite inside package.
1190+ - short forms of binary prefixes, more units and fix to less than comparison.
1191+ (Issue #20, thanks muggenhor)
1192+ - pint is now zip-safe
1193+ (Issue #23, thanks muggenhor)
1194+
1195+
1196+ Version 0.1.3 (2013-01-07)
1197+ --------------------------
1198+
1199+ - abbreviated quantity string formating.
1200+ - complete Python 2.7 compatibility.
1201+ - implemented pickle support for Quantities objects.
1202+ - extended NumPy support.
1203+ - various bugfixes.
1204+
1205+
1206+ Version 0.1.2 (2012-08-12)
1207+ --------------------------
1208+
1209+ - experimenal NumPy support.
1210+ - included default unit definitions file.
1211+ (Issue #1, thanks fish2000)
1212+ - better testing.
1213+ - various bugfixes.
1214+ - fixed some units definitions.
1215+ (Issue #4, thanks craigholm)
1216+
1217+
1218+ Version 0.1.1 (2012-07-31)
1219+ --------------------------
1220+
1221+ - better packaging and installation.
1222+
1223+
1224+ Version 0.1 (2012-07-26)
1225+ --------------------------
1226+
1227+ - first public release.
1228+
1229+Keywords: physical quantities unit conversion science
1230+Platform: UNKNOWN
1231+Classifier: Development Status :: 4 - Beta
1232+Classifier: Intended Audience :: Developers
1233+Classifier: Intended Audience :: Science/Research
1234+Classifier: License :: OSI Approved :: BSD License
1235+Classifier: Operating System :: MacOS :: MacOS X
1236+Classifier: Operating System :: Microsoft :: Windows
1237+Classifier: Operating System :: POSIX
1238+Classifier: Programming Language :: Python
1239+Classifier: Topic :: Scientific/Engineering
1240+Classifier: Topic :: Software Development :: Libraries
1241+Classifier: Programming Language :: Python :: 2.6
1242+Classifier: Programming Language :: Python :: 2.7
1243+Classifier: Programming Language :: Python :: 3.0
1244+Classifier: Programming Language :: Python :: 3.1
1245+Classifier: Programming Language :: Python :: 3.2
1246+Classifier: Programming Language :: Python :: 3.3
1247
1248=== added directory 'Pint.egg-info'
1249=== added file 'Pint.egg-info/PKG-INFO'
1250--- Pint.egg-info/PKG-INFO 1970-01-01 00:00:00 +0000
1251+++ Pint.egg-info/PKG-INFO 2015-01-07 15:51:38 +0000
1252@@ -0,0 +1,400 @@
1253+Metadata-Version: 1.1
1254+Name: Pint
1255+Version: 0.6
1256+Summary: Physical quantities module
1257+Home-page: https://github.com/hgrecco/pint
1258+Author: Hernan E. Grecco
1259+Author-email: hernan.grecco@gmail.com
1260+License: BSD
1261+Description: Pint: a Python units library
1262+ ============================
1263+
1264+ Pint is Python module/package to define, operate and manipulate physical
1265+ quantities: the product of a numerical value and a unit of measurement.
1266+ It allows arithmetic operations between them and conversions from and
1267+ to different units.
1268+
1269+ It is distributed with a comprehensive list of physical units, prefixes
1270+ and constants. Due to its modular design, you to extend (or even rewrite!)
1271+ the complete list without changing the source code.
1272+
1273+ It has a complete test coverage. It runs in Python 2.7 and 3.X
1274+ with no other dependency. It licensed under BSD.
1275+
1276+
1277+ Design principles
1278+ -----------------
1279+
1280+ Although there are already a few very good Python packages to handle physical
1281+ quantities, no one was really fitting my needs. Like most developers, I programed
1282+ Pint to scratch my own itches.
1283+
1284+ - Unit parsing: prefixed and pluralized forms of units are recognized without
1285+ explicitly defining them. In other words: as the prefix *kilo* and the unit *meter*
1286+ are defined, Pint understands *kilometers*. This results in a much shorter and
1287+ maintainable unit definition list as compared to other packages.
1288+
1289+ - Standalone unit definitions: units definitions are loaded from simple and
1290+ easy to edit text file. Adding and changing units and their definitions does
1291+ not involve changing the code.
1292+
1293+ - Advanced string formatting: a quantity can be formatted into string using
1294+ PEP 3101 syntax. Extended conversion flags are given to provide latex and pretty
1295+ formatting.
1296+
1297+ - Small codebase: small and easy to maintain with a flat hierarchy.
1298+
1299+ - Dependency free: it depends only on Python and its standard library.
1300+
1301+ - Python 2 and 3: A single codebase that runs unchanged in Python 2.7+ and Python 3.0+.
1302+
1303+ - Advanced NumPy support: While NumPy is not a requirement for Pint,
1304+ when available ndarray methods and ufuncs can be used in Quantity objects.
1305+
1306+
1307+ Pint is written and maintained by Hernan E. Grecco <hernan.grecco@gmail.com>.
1308+
1309+ Other contributors, listed alphabetically, are:
1310+
1311+ * Alexander Böhn <fish2000@gmail.com>
1312+ * Brend Wanders <b.wanders@utwente.nl>
1313+ * choloepus
1314+ * Daniel Sokolowski <daniel.sokolowski@danols.com>
1315+ * Dave Brooks <dave@bcs.co.nz>
1316+ * David Linke
1317+ * Eduard Bopp <eduard.bopp@aepsil0n.de>
1318+ * Felix Hummel <felix@felixhummel.de>
1319+ * Giel van Schijndel <me@mortis.eu>
1320+ * James Rowe <jnrowe@gmail.com>
1321+ * Jim Turner <jturner314@gmail.com>
1322+ * Joel B. Mohler <joel@kiwistrawberry.us>
1323+ * John David Reaver <jdreaver@adlerhorst.com>
1324+ * Jonas Olson <jolson@kth.se>
1325+ * Kenneth D. Mankoff <mankoff@gmail.com>
1326+ * Luke Campbell <luke.s.campbell@gmail.com>
1327+ * Matthieu Dartiailh <marul@laposte.net>
1328+ * Nate Bogdanowicz <natezb@gmail.com>
1329+ * Peter Grayson <jpgrayson@gmail.com>
1330+ * Richard Barnes <rbarnes@umn.edu>
1331+ * Ryan Kingsbury <RyanSKingsbury@alumni.unc.edu>
1332+ * Sundar Raman <cybertoast@gmail.com>
1333+ * Tiago Coutinho <coutinho@esrf.fr>
1334+ * Tom Ritchford <tom@swirly.com>
1335+ * Virgil Dupras <virgil.dupras@savoirfairelinux.com>
1336+
1337+ (If you think that your name belongs here, please let the maintainer know)
1338+
1339+
1340+ Pint Changelog
1341+ ==============
1342+
1343+
1344+ 0.6 (2014-11-07)
1345+ ----------------
1346+
1347+ - Fix operations with measurments and user defined units.
1348+ (Issue #204)
1349+ - Faster conversions through caching and other performance improvements.
1350+ (Issue #193, thanks MatthieuDartiailh)
1351+ - Better error messages on Quantity.__setitem__.
1352+ (Issue #191)
1353+ - Fixed abbreviation of fluid_ounce.
1354+ (Issue #187, thanks hsoft)
1355+ - Defined Angstrom symbol.
1356+ (Issue #181, thanks JonasOlson)
1357+ - Removed fetching version from git repo as it triggers XCode installation on OSX.
1358+ (Issue #178, thanks deanishe)
1359+ - Improved context documentation.
1360+ (Issue #176 and 179, thanks rsking84)
1361+ - Added Chemistry context.
1362+ (Issue #179, thanks rsking84)
1363+ - Fix help(UnitRegisty)
1364+ (Issue #168)
1365+ - Optimized "get_dimensionality" and "get_base_name".
1366+ (Issue #166 and #167, thanks jbmohler)
1367+ - Renamed ureg.parse_units parameter "to_delta" to "as_delta" to make clear.
1368+ that no conversion happens. Accordingly, the parameter/property
1369+ "default_to_delta" of UnitRegistry was renamed to "default_as_delta".
1370+ (Issue #158, thanks dalit)
1371+ - Fixed problem when adding two uncertainties.
1372+ (thanks dalito)
1373+ - Full support for Offset units (e.g. temperature)
1374+ (Issue #88, #143, #147 and #161, thanks dalito)
1375+
1376+
1377+ 0.5.2 (2014-07-31)
1378+ ------------------
1379+
1380+ - Changed travis config to use miniconda for faster testing.
1381+ - Added wheel configuration to setup.cfg.
1382+ - Ensure resource streams are closed after reading.
1383+ - Require setuptools.
1384+ (Issue #169)
1385+ - Implemented real, imag and T Quantity properties.
1386+ (Issue #171)
1387+ - Implemented __int__ and __long__ for Quantity
1388+ (Issue #170)
1389+ - Fixed SI prefix error on ureg.convert.
1390+ (Issue #156, thanks jdreaver)
1391+ - Fixed parsing of multiparemeter contexts.
1392+ (Issue #174)
1393+
1394+
1395+ 0.5.1 (2014-06-03)
1396+ ------------------
1397+
1398+ - Implemented a standard way to change the registry used in unpickling operations.
1399+ (Issue #148)
1400+ - Fix bug where conversion would fail due to caching.
1401+ (Issue #140, thanks jdreaver)
1402+ - Allow assigning Not a Number to a quantity array.
1403+ (Issue #127)
1404+ - Decoupled Quantity in place and not in place unit conversion methods.
1405+ - Return None in functions that modify quantities in place.
1406+ - Improved testing infrastructure to check for unwanted warnings.
1407+ - Added test function at the package level to run all tests.
1408+
1409+
1410+ 0.5 (2014-05-07)
1411+ ----------------
1412+
1413+ - Improved test suite helper functions.
1414+ - Print honors default format w/o format().
1415+ (Issue #132, thanks mankoff)
1416+ - Fixed sum() by treating number zero as a special case.
1417+ (Issue #122, thanks rec)
1418+ - Improved behaviour in ScaleConverter, OffsetConverter and Quantity.to.
1419+ (Issue #120)
1420+ - Reimplemented loading of default definitions to allow Pint in a cx_freeze or similar package.
1421+ (Issue #118, thanks jbmohler)
1422+ - Implemented parsing of pretty printed units.
1423+ (Issue #117, thanks jpgrayson)
1424+ - Fixed representation of dimensionless quantities.
1425+ (Issue #112, thanks rec)
1426+ - Raise error when invalid formatting code is given.
1427+ (Issue #111, thanks rec)
1428+ - Default registry to lazy load, raise error on redefinition
1429+ (Issue #108, thanks rec, aepsil0n)
1430+ - Added condensed format.
1431+ (Issue #107, thanks rec)
1432+ - Added UnitRegistry () operator to parse expression replacing [].
1433+ (Issue #106, thanks rec)
1434+ - Optional case insensitive unit parsing.
1435+ (Issue #105, thanks rec, jeremyfreeman, dbrnz)
1436+ - Change the Quantity mutability depending on magnitude type.
1437+ (Issue #104, thanks rec)
1438+ - Implemented API to list compatible units.
1439+ (Issue #89)
1440+ - Implemented cache of key UnitRegistry methods.
1441+ - Rewrote the Measurement class to use uncertainties.
1442+ (Issue #24)
1443+
1444+
1445+ 0.4.2 (2014-02-14)
1446+ ------------------
1447+
1448+ - Python 2.6 support
1449+ (Issue #96, thanks tiagocoutinho)
1450+ - Fixed symbol for inch.
1451+ (Issue #102, thanks cybertoast)
1452+ - Stop raising AttributeError when wrapping funcs without all of the attributes.
1453+ (Issue #100, thanks jturner314)
1454+ - Fixed warning appearing in Py2.x when comparing a Numpy Array with an empty string.
1455+ (Issue #98, thanks jturner314)
1456+ - Add links to AUR packages in docs.
1457+ (Issue #91, thanks jturner314)
1458+ - Fixed garbage collection related problem.
1459+ (Issue #92, thanks jturner314)
1460+
1461+
1462+ 0.4.1 (2014-01-12)
1463+ ------------------
1464+
1465+ - Integer Division with Arrays.
1466+ (Issue #80, thanks jdreaver)
1467+ - Improved Documentation.
1468+ (Issue #83, thanks choloepus)
1469+ - Removed 'h' alias for hour due to conflict with Planck's constant.
1470+ (Issue #82, thanks choloepus)
1471+ - Improved get_base_units for non-multiplicative units.
1472+ (Issue #85, thanks exxus)
1473+ - Refactored code for multiplication.
1474+ (Issue #84, thanks jturner314)
1475+ - Removed 'R' alias for roentgen as it collides with molar_gas_constant.
1476+ (Issue #87, thanks rsking84)
1477+ - Improved naming of temperature units and multiplication of non-multiplicative units.
1478+ (Issue #86, tahsnk exxus)
1479+
1480+
1481+
1482+ 0.4 (2013-12-17)
1483+ ----------------
1484+
1485+ - Introduced Contexts: relation between incompatible dimensions.
1486+ (Issue #65)
1487+ - Fixed get_base_units for non multiplicative units.
1488+ (Related to issue #66)
1489+ - Implemented default formatting for quantities.
1490+ - Changed comparison between Quantities containing NumPy arrays.
1491+ (Issue #75) - BACKWARDS INCOMPATIBLE CHANGE
1492+ - Fixes for NumPy 1.8 due to changes in handling binary ops.
1493+ (Issue #73)
1494+
1495+
1496+ 0.3.3 (2013-11-29)
1497+ ------------------
1498+
1499+ - ParseHelper can now parse units named like python keywords.
1500+ (Issue #69)
1501+ - Fix comparison of quantities.
1502+ (Issue #74)
1503+ - Fix Inequality operator.
1504+ (Issue #70, thanks muggenhor)
1505+ - Improved travis configuration.
1506+ (thanks muggenhor)
1507+
1508+
1509+ 0.3.2 (2013-10-22)
1510+ ------------------
1511+
1512+ - Fix get_dimensionality for non multiplicative units.
1513+ (Issue #66)
1514+ - Proper handling of @import directive inside a file read using pkg_resources.
1515+ (Issue #68)
1516+
1517+
1518+ 0.3.1 (2013-09-15)
1519+ ------------------
1520+
1521+ - fix right division on python 2.7
1522+ (Issue #58, thanks natezb)
1523+ - fix formatting of fractional exponentials between 0 and 1.
1524+ (Issue #62, thanks jdreaver)
1525+ - fix installation as egg.
1526+ (Issue #61)
1527+ - fix handling of strange values as input of Quantity.
1528+ (Issue #53)
1529+ - math operations between quantities of different registries now raise a ValueError.
1530+ (Issue #52)
1531+
1532+
1533+ 0.3 (2013-09-02)
1534+ ----------------
1535+
1536+ - support for IPython autocomplete and rich display.
1537+ (Issues #30 and #31)
1538+ - support for @import directive in definitions file.
1539+ (Issue #22)
1540+ - support for wrapping functions to make them pint-aware.
1541+ (Issue #16)
1542+ - support for comparing UnitsContainer to string.
1543+ (Issue #35)
1544+ - fix error raised while converting from a single unit to one expressed as
1545+ the relation between many.
1546+ (Issue #29)
1547+ - fix error raised when unit symbol is missing.
1548+ (Issue #41)
1549+ - fix error raised when magnitude is Decimal.
1550+ (Issue #46, thanks danielsokolowski)
1551+ - support for non-installed pint.
1552+ (Issue #42, thanks danielsokolowski)
1553+ - support for application of numpy function on non-ndarray magnitudes.
1554+ (Issue #44)
1555+ - support for math operations on dimensionless Quantities (written with units).
1556+ (Issue #45)
1557+ - fix obtaining dimensionless quantity from string.
1558+ (Issue #50)
1559+ - fix adding and comparing numbers to a dimensionless quantity (written with units).
1560+ (Issue #54)
1561+ - Support for iter in Quantity.
1562+ (Issue #55, thanks natezb)
1563+
1564+
1565+ 0.2.1 (2013-07-02)
1566+ ------------------
1567+
1568+ - fix error raised while converting from a single unit to one expressed as
1569+ the relation between many.
1570+ (Issue #29)
1571+
1572+
1573+ 0.2 (2013-05-13)
1574+ ----------------
1575+
1576+ - support for Measurement (Quantity +/- error).
1577+ - implemented buckingham pi theorem for dimensional analysis.
1578+ - support for temperature units and temperature difference units.
1579+ - parser can infers if the user mean temperature or temperature difference.
1580+ - support for derived dimensions (e.g. [speed] = [length] / [time]).
1581+ - refactored the code into multiple files.
1582+ - refactored code to isolate definitions and converters.
1583+ - refactored formatter out of UnitParser class.
1584+ - added tox and travis config files for CI.
1585+ - comprehensive NumPy testing including almost all ufuncs.
1586+ - full NumPy support (features is not longer experimental).
1587+ - fixed bug preventing from having independent registries.
1588+ (Issue #10, thanks bwanders)
1589+ - forces real division as default for Quantities.
1590+ (Issue #7, thanks dbrnz)
1591+ - improved default unit definition file.
1592+ (Issue #13, thanks r-barnes)
1593+ - smarter parser supporting spaces as multiplications and other nice features.
1594+ (Issue #13, thanks r-barnes)
1595+ - moved testsuite inside package.
1596+ - short forms of binary prefixes, more units and fix to less than comparison.
1597+ (Issue #20, thanks muggenhor)
1598+ - pint is now zip-safe
1599+ (Issue #23, thanks muggenhor)
1600+
1601+
1602+ Version 0.1.3 (2013-01-07)
1603+ --------------------------
1604+
1605+ - abbreviated quantity string formating.
1606+ - complete Python 2.7 compatibility.
1607+ - implemented pickle support for Quantities objects.
1608+ - extended NumPy support.
1609+ - various bugfixes.
1610+
1611+
1612+ Version 0.1.2 (2012-08-12)
1613+ --------------------------
1614+
1615+ - experimenal NumPy support.
1616+ - included default unit definitions file.
1617+ (Issue #1, thanks fish2000)
1618+ - better testing.
1619+ - various bugfixes.
1620+ - fixed some units definitions.
1621+ (Issue #4, thanks craigholm)
1622+
1623+
1624+ Version 0.1.1 (2012-07-31)
1625+ --------------------------
1626+
1627+ - better packaging and installation.
1628+
1629+
1630+ Version 0.1 (2012-07-26)
1631+ --------------------------
1632+
1633+ - first public release.
1634+
1635+Keywords: physical quantities unit conversion science
1636+Platform: UNKNOWN
1637+Classifier: Development Status :: 4 - Beta
1638+Classifier: Intended Audience :: Developers
1639+Classifier: Intended Audience :: Science/Research
1640+Classifier: License :: OSI Approved :: BSD License
1641+Classifier: Operating System :: MacOS :: MacOS X
1642+Classifier: Operating System :: Microsoft :: Windows
1643+Classifier: Operating System :: POSIX
1644+Classifier: Programming Language :: Python
1645+Classifier: Topic :: Scientific/Engineering
1646+Classifier: Topic :: Software Development :: Libraries
1647+Classifier: Programming Language :: Python :: 2.6
1648+Classifier: Programming Language :: Python :: 2.7
1649+Classifier: Programming Language :: Python :: 3.0
1650+Classifier: Programming Language :: Python :: 3.1
1651+Classifier: Programming Language :: Python :: 3.2
1652+Classifier: Programming Language :: Python :: 3.3
1653
1654=== added file 'Pint.egg-info/SOURCES.txt'
1655--- Pint.egg-info/SOURCES.txt 1970-01-01 00:00:00 +0000
1656+++ Pint.egg-info/SOURCES.txt 2015-01-07 15:51:38 +0000
1657@@ -0,0 +1,72 @@
1658+AUTHORS
1659+CHANGES
1660+CHANGES_DEV
1661+LICENSE
1662+MANIFEST.in
1663+README
1664+setup.cfg
1665+setup.py
1666+Pint.egg-info/PKG-INFO
1667+Pint.egg-info/SOURCES.txt
1668+Pint.egg-info/dependency_links.txt
1669+Pint.egg-info/entry_points.txt
1670+Pint.egg-info/top_level.txt
1671+Pint.egg-info/zip-safe
1672+bench/bench.py
1673+bench/bench_base.yaml
1674+bench/bench_numpy.yaml
1675+docs/Makefile
1676+docs/conf.py
1677+docs/contexts.rst
1678+docs/contributing.rst
1679+docs/defining.rst
1680+docs/faq.rst
1681+docs/getting.rst
1682+docs/index.rst
1683+docs/make.bat
1684+docs/measurement.rst
1685+docs/nonmult.rst
1686+docs/numpy.rst
1687+docs/pitheorem.rst
1688+docs/serialization.rst
1689+docs/tutorial.rst
1690+docs/wrapping.rst
1691+docs/_static/logo-full.jpg
1692+docs/_templates/sidebarintro.html
1693+docs/_templates/sidebarlogo.html
1694+docs/_themes/.gitignore
1695+docs/_themes/LICENSE
1696+docs/_themes/README
1697+docs/_themes/flask_theme_support.py
1698+docs/_themes/flask/layout.html
1699+docs/_themes/flask/relations.html
1700+docs/_themes/flask/theme.conf
1701+docs/_themes/flask/static/flasky.css_t
1702+docs/_themes/flask/static/small_flask.css
1703+pint/__init__.py
1704+pint/constants_en.txt
1705+pint/context.py
1706+pint/default_en.txt
1707+pint/formatting.py
1708+pint/measurement.py
1709+pint/quantity.py
1710+pint/unit.py
1711+pint/util.py
1712+pint/compat/__init__.py
1713+pint/compat/chainmap.py
1714+pint/compat/lrucache.py
1715+pint/compat/nullhandler.py
1716+pint/compat/transformdict.py
1717+pint/testsuite/__init__.py
1718+pint/testsuite/helpers.py
1719+pint/testsuite/parameterized.py
1720+pint/testsuite/test_contexts.py
1721+pint/testsuite/test_formatter.py
1722+pint/testsuite/test_issues.py
1723+pint/testsuite/test_measurement.py
1724+pint/testsuite/test_numpy.py
1725+pint/testsuite/test_pitheorem.py
1726+pint/testsuite/test_quantity.py
1727+pint/testsuite/test_umath.py
1728+pint/testsuite/test_unit.py
1729+pint/testsuite/test_util.py
1730\ No newline at end of file
1731
1732=== added file 'Pint.egg-info/dependency_links.txt'
1733--- Pint.egg-info/dependency_links.txt 1970-01-01 00:00:00 +0000
1734+++ Pint.egg-info/dependency_links.txt 2015-01-07 15:51:38 +0000
1735@@ -0,0 +1,1 @@
1736+
1737
1738=== added file 'Pint.egg-info/entry_points.txt'
1739--- Pint.egg-info/entry_points.txt 1970-01-01 00:00:00 +0000
1740+++ Pint.egg-info/entry_points.txt 2015-01-07 15:51:38 +0000
1741@@ -0,0 +1,3 @@
1742+[zest.releaser.releaser.after_checkout]
1743+pyroma = pint:_run_pyroma
1744+
1745
1746=== added file 'Pint.egg-info/top_level.txt'
1747--- Pint.egg-info/top_level.txt 1970-01-01 00:00:00 +0000
1748+++ Pint.egg-info/top_level.txt 2015-01-07 15:51:38 +0000
1749@@ -0,0 +1,1 @@
1750+pint
1751
1752=== added file 'Pint.egg-info/zip-safe'
1753--- Pint.egg-info/zip-safe 1970-01-01 00:00:00 +0000
1754+++ Pint.egg-info/zip-safe 2015-01-07 15:51:38 +0000
1755@@ -0,0 +1,1 @@
1756+
1757
1758=== modified file 'debian/changelog'
1759--- debian/changelog 2014-09-05 23:26:05 +0000
1760+++ debian/changelog 2015-01-07 15:51:38 +0000
1761@@ -1,3 +1,10 @@
1762+python-pint (0.6-0ubuntu1) UNRELEASED; urgency=medium
1763+
1764+ * New upstream release.
1765+ * d/control: Add python-numpy and python3-numpy to Build-Depends.
1766+
1767+ -- Corey Bryant <corey.bryant@canonical.com> Wed, 07 Jan 2015 10:47:04 -0500
1768+
1769 python-pint (0.5.2-1) unstable; urgency=medium
1770
1771 * Initial release. (Closes: #760586)
1772
1773=== modified file 'debian/control'
1774--- debian/control 2014-09-05 23:26:05 +0000
1775+++ debian/control 2015-01-07 15:51:38 +0000
1776@@ -6,11 +6,13 @@
1777 Build-Depends: debhelper (>= 9),
1778 python-all (>= 2.6.6-3~),
1779 python-nose,
1780+ python-numpy,
1781 python-setuptools,
1782 python-sphinx,
1783 python-yaml,
1784 python3-all,
1785 python3-nose,
1786+ python3-numpy,
1787 python3-setuptools,
1788 python3-yaml
1789 Standards-Version: 3.9.5
1790
1791=== modified file 'debian/watch'
1792--- debian/watch 2014-09-05 23:26:05 +0000
1793+++ debian/watch 2015-01-07 15:51:38 +0000
1794@@ -1,3 +1,3 @@
1795 version=3
1796-http://pypi.python.org/packages/source/P/Pint Pint-(.*).tar.gz
1797-
1798+opts="uversionmangle=s/.zip//" \
1799+ http://pypi.python.org/packages/source/P/Pint/ Pint-(.*).zip
1800
1801=== modified file 'docs/_themes/flask/layout.html'
1802--- docs/_themes/flask/layout.html 2014-09-05 23:26:05 +0000
1803+++ docs/_themes/flask/layout.html 2015-01-07 15:51:38 +0000
1804@@ -10,7 +10,7 @@
1805 {%- block relbar2 %}
1806 {% if theme_github_fork %}
1807 <a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
1808- src="" alt="Fork me on GitHub" /></a>
1809+ src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
1810 {% endif %}
1811 {% endblock %}
1812 {% block header %}
1813
1814=== modified file 'docs/conf.py'
1815--- docs/conf.py 2014-09-05 23:26:05 +0000
1816+++ docs/conf.py 2015-01-07 15:51:38 +0000
1817@@ -28,7 +28,7 @@
1818
1819 # Add any Sphinx extension module names here, as strings. They can be extensions
1820 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
1821-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
1822+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax']
1823
1824 # Add any paths that contain templates here, relative to this directory.
1825 templates_path = ['_templates']
1826
1827=== modified file 'docs/contexts.rst'
1828--- docs/contexts.rst 2014-09-05 23:26:05 +0000
1829+++ docs/contexts.rst 2015-01-07 15:51:38 +0000
1830@@ -108,6 +108,15 @@
1831 >>> f.to('nm', 'sp', n=1.33)
1832 <Quantity(398.496240602, 'nanometer')>
1833
1834+Contexts can also accept Pint Quantity objects as parameters. For example, the 'chemistry'
1835+context accepts the molecular weight of a substance (as a Quantity with dimensions of
1836+[mass]/[substance]) to allow conversion between moles and mass.
1837+
1838+.. doctest::
1839+
1840+ >>> substance = 95 * ureg('g')
1841+ >>> moles = substance.to('moles', 'chemistry', mw = 5 * ureg('g/mol'))
1842+ <Quantity(19.0, 'mole')>
1843
1844
1845 Defining contexts in a file
1846@@ -129,7 +138,8 @@
1847 All parameters are named and default values are mandatory. Multiple parameters
1848 are separated by commas (like in a python function definition). Finally, you provide the name
1849 of the context (e.g. spectroscopy) and, optionally, a short version of the name (e.g. sp)
1850-separated by an equal sign.
1851+separated by an equal sign. See the definition of the 'chemistry' context in default_en.txt
1852+for an example of a multiple-parameter context.
1853
1854 Conversions rules are specified by providing source and destination dimensions separated
1855 using a colon (`:`) from the equation. A special variable named `value` will be replaced
1856@@ -140,6 +150,8 @@
1857 from the first dimension to the second one. A double arrow (`<->`) is used to
1858 indicate that the transformation operates both ways.
1859
1860+Context definitions are stored and imported exactly like custom units definition file
1861+(and can be included in the same file as unit definitions). See "Defining units" for details.
1862
1863 Defining contexts programmatically
1864 ----------------------------------
1865
1866=== modified file 'docs/faq.rst'
1867--- docs/faq.rst 2014-09-05 23:26:05 +0000
1868+++ docs/faq.rst 2015-01-07 15:51:38 +0000
1869@@ -13,6 +13,8 @@
1870 You mention other similar Python libraries. Can you point me to those?
1871 ----------------------------------------------------------------------
1872
1873+`natu <http://kdavies4.github.io/natu/>`_
1874+
1875 `Buckingham <https://code.google.com/p/buckingham/>`_
1876
1877 `Magnitude <http://github.com/juanre/magnitude.git>`_
1878
1879=== modified file 'docs/index.rst'
1880--- docs/index.rst 2014-09-05 23:26:05 +0000
1881+++ docs/index.rst 2015-01-07 15:51:38 +0000
1882@@ -9,7 +9,7 @@
1883
1884 Pint is Python package to define, operate and manipulate **physical quantities**: the product of a numerical value and a unit of measurement. It allows arithmetic operations between them and conversions from and to different units.
1885
1886-It is distributed with a comprehensive list of physical units, prefixes and constants. Due to its modular design, you can extend (or even rewrite!) the complete list without changing the source code.
1887+It is distributed with a comprehensive list of physical units, prefixes and constants. Due to its modular design, you can extend (or even rewrite!) the complete list without changing the source code. It supports a lot of numpy mathematical operations without monkey patching or wrapping numpy.
1888
1889 It has a complete test coverage. It runs in Python 2.6+ and 3.2+ with no other dependency. It licensed under BSD.
1890
1891
1892=== modified file 'docs/nonmult.rst'
1893--- docs/nonmult.rst 2014-09-05 23:26:05 +0000
1894+++ docs/nonmult.rst 2015-01-07 15:51:38 +0000
1895@@ -4,17 +4,17 @@
1896 Temperature conversion
1897 ======================
1898
1899-Unlike meters and seconds, fahrenheits, celsius and kelvin are not
1900-multiplicative units. Temperature is expressed in a system with a
1901-reference point, and relations between temperature units include
1902-not only an scaling factor but also an offset. Pint supports these
1903-type of units and conversions between them. The default definition
1904-file includes fahrenheits, celsius, kelvin and rankine abbreviated
1905-as degF, degC, degK, and degR.
1906+Unlike meters and seconds, the temperature units fahrenheits and
1907+celsius are non-multiplicative units. These temperature units are
1908+expressed in a system with a reference point, and relations between
1909+temperature units include not only a scaling factor but also an offset.
1910+Pint supports these type of units and conversions between them.
1911+The default definition file includes fahrenheits, celsius,
1912+kelvin and rankine abbreviated as degF, degC, degK, and degR.
1913
1914 For example, to convert from celsius to fahrenheit:
1915
1916-.. testsetup:: *
1917+.. testsetup::
1918
1919 from pint import UnitRegistry
1920 ureg = UnitRegistry()
1921@@ -24,21 +24,23 @@
1922
1923 >>> from pint import UnitRegistry
1924 >>> ureg = UnitRegistry()
1925- >>> home = 25.4 * ureg.degC
1926+ >>> Q_ = ureg.Quantity
1927+ >>> home = Q_(25.4, ureg.degC)
1928 >>> print(home.to('degF'))
1929- 77.72000039999993 degF
1930+ 77.7200004 degF
1931
1932 or to other kelvin or rankine:
1933
1934 .. doctest::
1935
1936- >>> print(home.to('degK'))
1937- 298.54999999999995 degK
1938+ >>> print(home.to('kelvin'))
1939+ 298.55 kelvin
1940 >>> print(home.to('degR'))
1941 537.39 degR
1942
1943-Additionally, for every temperature unit in the registry,
1944-there is also a *delta* counterpart to specify differences.
1945+Additionally, for every non-multiplicative temperature unit
1946+in the registry, there is also a *delta* counterpart to specify
1947+differences. Absolute units have no *delta* counterpart.
1948 For example, the change in celsius is equal to the change
1949 in kelvin, but not in fahrenheit (as the scaling factor
1950 is different).
1951@@ -46,20 +48,61 @@
1952 .. doctest::
1953
1954 >>> increase = 12.3 * ureg.delta_degC
1955- >>> print(increase.to(ureg.delta_degK))
1956- 12.3 delta_degK
1957+ >>> print(increase.to(ureg.kelvin))
1958+ 12.3 kelvin
1959 >>> print(increase.to(ureg.delta_degF))
1960- 6.833333333333334 delta_degF
1961+ 22.14 delta_degF
1962
1963 ..
1964- Subtraction of two temperatures also yields a *delta* unit.
1965-
1966- .. doctest::
1967-
1968- >>> 25.4 * ureg.degC - 10. * ureg.degC
1969- 15.4 delta_degC
1970-
1971-Differences in temperature are multiplicative:
1972+Subtraction of two temperatures given in offset units yields a *delta* unit:
1973+
1974+.. doctest::
1975+
1976+ >>> Q_(25.4, ureg.degC) - Q_(10., ureg.degC)
1977+ <Quantity(15.4, 'delta_degC')>
1978+
1979+You can add or subtract a quantity with *delta* unit and a quantity with
1980+offset unit:
1981+
1982+.. doctest::
1983+
1984+ >>> Q_(25.4, ureg.degC) + Q_(10., ureg.delta_degC)
1985+ <Quantity(35.4, 'degC')>
1986+ >>> Q_(25.4, ureg.degC) - Q_(10., ureg.delta_degC)
1987+ <Quantity(15.4, 'degC')>
1988+
1989+If you want to add a quantity with absolute unit to one with offset unit, like here
1990+
1991+.. doctest::
1992+
1993+ >>> heating_rate = 0.5 * ureg.kelvin/ureg.min
1994+ >>> Q_(10., ureg.degC) + heating_rate * Q_(30, ureg.min)
1995+ Traceback (most recent call last):
1996+ ...
1997+ pint.unit.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC, kelvin).
1998+
1999+you have to avoid the ambiguity by either converting the offset unit to the
2000+absolute unit before addition
2001+
2002+.. doctest::
2003+
2004+ >>> Q_(10., ureg.degC).to(ureg.kelvin) + heating_rate * Q_(30, ureg.min)
2005+ <Quantity(298.15, 'kelvin')>
2006+
2007+or convert the absolute unit to a *delta* unit:
2008+
2009+.. doctest::
2010+
2011+ >>> Q_(10., ureg.degC) + heating_rate.to('delta_degC/min') * Q_(30, ureg.min)
2012+ <Quantity(25.0, 'degC')>
2013+
2014+In contrast to subtraction, the addition of quantities with offset units
2015+is ambiguous, e.g. for *10 degC + 100 degC* two different result are reasonable
2016+depending on the context, *110 degC* or *383.15 °C (= 283.15 K + 373.15 K)*.
2017+Because of this ambiguity pint raises an error for the addition of two
2018+quantities with offset units (since pint-0.6).
2019+
2020+Quantities with *delta* units are multiplicative:
2021
2022 .. doctest::
2023
2024@@ -67,7 +110,55 @@
2025 >>> print(speed.to('delta_degC/second'))
2026 1.0 delta_degC / second
2027
2028-The parser knows about *delta* units and use them when a temperature unit
2029+However, multiplication, division and exponentiation of quantities with
2030+offset units is problematic just like addition. Pint (since version 0.6)
2031+will by default raise an error when a quantity with offset unit is used in
2032+these operations. Due to this quantities with offset units cannot be created
2033+like other quantities by multiplication of magnitude and unit but have
2034+to be explicitly created:
2035+
2036+.. doctest::
2037+
2038+ >>> home = 25.4 * ureg.degC
2039+ Traceback (most recent call last):
2040+ ...
2041+ pint.unit.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).
2042+ >>> Q_(25.4, ureg.degC)
2043+ <Quantity(25.4, 'degC')>
2044+
2045+As an alternative to raising an error, pint can be configured to work more
2046+relaxed via setting the UnitRegistry parameter *autoconvert_offset_to_baseunit*
2047+to true. In this mode, pint behaves differently:
2048+
2049+* Multiplication of a quantity with a single offset unit with order +1 by
2050+ a number or ndarray yields the quantity in the given unit.
2051+
2052+.. doctest::
2053+
2054+ >>> ureg = UnitRegistry(autoconvert_offset_to_baseunit = True)
2055+ >>> T = 25.4 * ureg.degC
2056+ >>> T
2057+ <Quantity(25.4, 'degC')>
2058+
2059+* Before all other multiplications, all divisions and in case of
2060+ exponentiation [#f1]_ involving quantities with offset-units, pint
2061+ will convert the quantities with offset units automatically to the
2062+ corresponding base unit before performing the operation.
2063+
2064+ >>> 1/T
2065+ <Quantity(0.00334952269302, '1 / kelvin')>
2066+ >>> T * 10 * ureg.meter
2067+ <Quantity(527.15, 'kelvin * meter')>
2068+
2069+You can change the behaviour at any time:
2070+
2071+ >>> ureg.autoconvert_offset_to_baseunit = False
2072+ >>> 1/T
2073+ Traceback (most recent call last):
2074+ ...
2075+ pint.unit.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).
2076+
2077+The parser knows about *delta* units and uses them when a temperature unit
2078 is found in a multiplicative context. For example, here:
2079
2080 .. doctest::
2081@@ -86,9 +177,15 @@
2082
2083 .. doctest::
2084
2085- >>> print(ureg.parse_units('degC/meter', to_delta=False))
2086+ >>> print(ureg.parse_units('degC/meter', as_delta=False))
2087 degC / meter
2088
2089+Note that the magnitude is left unchanged:
2090+
2091+.. doctest::
2092+
2093+ >>> Q_(10, 'degC/meter')
2094+ <Quantity(10, 'delta_degC / meter')>
2095
2096 To define a new temperature, you need to specify the offset. For example,
2097 this is the definition of the celsius and fahrenheit::
2098@@ -98,3 +195,5 @@
2099
2100 You do not need to define *delta* units, as they are defined automatically.
2101
2102+.. [#f1] If the exponent is +1, the quantity will not be converted to base
2103+ unit but remains unchanged.
2104\ No newline at end of file
2105
2106=== modified file 'docs/serialization.rst'
2107--- docs/serialization.rst 2014-09-05 23:26:05 +0000
2108+++ docs/serialization.rst 2015-01-07 15:51:38 +0000
2109@@ -19,7 +19,7 @@
2110 >>> import pint
2111 >>> ureg = pint.UnitRegistry()
2112 >>> duration = 24.2 * ureg.years
2113- >>> print(duration)
2114+ >>> duration
2115 <Quantity(24.2, 'year')>
2116 >>> serialized = str(duration)
2117 >>> print(serialized)
2118@@ -37,7 +37,7 @@
2119 >>> ureg = pint.UnitRegistry()
2120 >>> duration = ureg('24.2 year')
2121 >>> print(duration)
2122- <Quantity(24.2, 'year')>
2123+ 24.2 year
2124
2125 Notice that the serialized quantity is likely to be parsed in **another** registry
2126 as shown in this example. Pint Quantities do not exist on their own but they are
2127@@ -72,6 +72,7 @@
2128
2129 >>> magnitude, units = pickle.loads(serialized)
2130 >>> ureg.Quantity(magnitude, units)
2131+ <Quantity(24.2, 'year')>
2132
2133 You can use the same mechanism with any serialization protocol, not only with binary ones.
2134 (In fact, version 0 of the Pickle protocol is ascii). Other common serialization protocols/packages
2135
2136=== modified file 'docs/tutorial.rst'
2137--- docs/tutorial.rst 2014-09-05 23:26:05 +0000
2138+++ docs/tutorial.rst 2015-01-07 15:51:38 +0000
2139@@ -96,7 +96,6 @@
2140 >>> print(height)
2141 5.75 foot
2142 >>> height.ito_base_units()
2143- <Quantity(1.7526, 'meter')>
2144 >>> print(height)
2145 1.7526 meter
2146
2147
2148=== modified file 'pint/__init__.py'
2149--- pint/__init__.py 2014-09-05 23:26:05 +0000
2150+++ pint/__init__.py 2015-01-07 15:51:38 +0000
2151@@ -16,25 +16,19 @@
2152 import subprocess
2153 import pkg_resources
2154 from .formatting import formatter
2155-from .unit import UnitRegistry, DimensionalityError, UndefinedUnitError, LazyRegistry
2156+from .unit import (UnitRegistry, DimensionalityError, OffsetUnitCalculusError,
2157+ UndefinedUnitError, LazyRegistry)
2158 from .util import pi_theorem, logger
2159
2160 from .context import Context
2161
2162
2163-__version__ = "unknown"
2164-try: # pragma: no cover
2165- # try to grab the commit version of our package
2166- __version__ = (subprocess.check_output(["git", "describe"],
2167- stderr=subprocess.STDOUT,
2168- cwd=os.path.dirname(os.path.abspath(__file__)))).strip()
2169-except: # pragma: no cover
2170- # on any error just try to grab the version that is installed on the system
2171- try:
2172- __version__ = pkg_resources.get_distribution('pint').version
2173- except: # pragma: no cover
2174- pass # we seem to have a local copy without any repository control or installed without setuptools
2175- # so the reported version will be __unknown__
2176+try: # pragma: no cover
2177+ __version__ = pkg_resources.get_distribution('pint').version
2178+except: # pragma: no cover
2179+ # we seem to have a local copy not installed without setuptools
2180+ # so the reported version will be unknown
2181+ __version__ = "unknown"
2182
2183
2184 #: A Registry with the default units and constants.
2185@@ -81,6 +75,47 @@
2186 sys.exit(1)
2187
2188
2189+def _check_travis(data): # pragma: no cover
2190+ """Check if Travis reports that everything is ok.
2191+ (used to perform checks before releasing a new version).
2192+ """
2193+ import json
2194+ import sys
2195+
2196+ from zest.releaser.utils import system, ask
2197+ if not ask('Check with Travis before releasing?'):
2198+ return
2199+
2200+ try:
2201+ # Python 3
2202+ from urllib.request import urlopen
2203+ def get(url):
2204+ return urlopen(url).read().decode('utf-8')
2205+
2206+ except ImportError:
2207+ # Python 2
2208+ from urllib2 import urlopen
2209+ def get(url):
2210+ return urlopen(url).read()
2211+
2212+ url = 'https://api.github.com/repos/%s/%s/status/%s'
2213+
2214+ username = 'hgrecco'
2215+ repo = 'pint'
2216+ commit = system('git rev-parse HEAD')
2217+
2218+ try:
2219+ result = json.loads(get(url % (username, repo, commit)))['state']
2220+ print('Travis says: %s' % result)
2221+ if result != 'success':
2222+ if not ask('Do you want to continue anyway?', default=False):
2223+ sys.exit(1)
2224+ except Exception:
2225+ print('Could not determine the commit state with Travis.')
2226+ if ask('Do you want to continue anyway?', default=False):
2227+ sys.exit(1)
2228+
2229+
2230 def test():
2231 """Run all tests.
2232
2233
2234=== modified file 'pint/default_en.txt'
2235--- pint/default_en.txt 2014-09-05 23:26:05 +0000
2236+++ pint/default_en.txt 2015-01-07 15:51:38 +0000
2237@@ -10,7 +10,7 @@
2238 femto- = 1e-15 = f-
2239 pico- = 1e-12 = p-
2240 nano- = 1e-9 = n-
2241-micro- = 1e-6 = u-
2242+micro- = 1e-6 = u- = µ-
2243 milli- = 1e-3 = m-
2244 centi- = 1e-2 = c-
2245 deci- = 1e-1 = d-
2246@@ -76,7 +76,7 @@
2247 coulomb = ampere * second = C
2248 volt = joule / coulomb = V
2249 farad = coulomb / volt = F
2250-ohm = volt / ampere
2251+ohm = volt / ampere = Ω
2252 siemens = ampere / volt = S = mho
2253 weber = volt * second = Wb
2254 tesla = weber / meter ** 2 = T
2255@@ -138,7 +138,7 @@
2256 baud = bit / second = Bd = bps
2257
2258 # Length
2259-angstrom = 1e-10 * meter
2260+angstrom = 1e-10 * meter = ångström = Å
2261 inch = 2.54 * centimeter = in = international_inch = inches = international_inches
2262 foot = 12 * inch = ft = international_foot = feet = international_feet
2263 mile = 5280 * foot = mi = international_mile
2264@@ -291,7 +291,7 @@
2265 pint = quart / 2 = pt = liquid_pint = US_liquid_pint
2266 cup = pint / 2 = liquid_cup = US_liquid_cup
2267 gill = cup / 2 = liquid_gill = US_liquid_gill
2268-floz = gill / 4 = fluid_ounce = US_fluid_ounce = US_liquid_ounce
2269+fluid_ounce = gill / 4 = floz = US_fluid_ounce = US_liquid_ounce
2270 imperial_bushel = 36.36872 * liter = UK_bushel
2271 imperial_gallon = imperial_bushel / 8 = UK_gallon
2272 imperial_quart = imperial_gallon / 4 = UK_quart
2273@@ -318,3 +318,33 @@
2274 [temperature] -> [energy]: boltzmann_constant * value
2275 [energy] -> [temperature]: value / boltzmann_constant
2276 @end
2277+
2278+@context(mw=0,volume=0,solvent_mass=0) chemistry = chem
2279+ # mw is the molecular weight of the species
2280+ # volume is the volume of the solution
2281+ # solvent_mass is the mass of solvent in the solution
2282+
2283+ # moles -> mass require the molecular weight
2284+ [substance] -> [mass]: value * mw
2285+ [mass] -> [substance]: value / mw
2286+
2287+ # moles/volume -> mass/volume and moles/mass -> mass / mass
2288+ # require the molecular weight
2289+ [substance] / [volume] -> [mass] / [volume]: value * mw
2290+ [mass] / [volume] -> [substance] / [volume]: value / mw
2291+ [substance] / [mass] -> [mass] / [mass]: value * mw
2292+ [mass] / [mass] -> [substance] / [mass]: value / mw
2293+
2294+ # moles/volume -> moles requires the solution volume
2295+ [substance] / [volume] -> [substance]: value * volume
2296+ [substance] -> [substance] / [volume]: value / volume
2297+
2298+ # moles/mass -> moles requires the solvent (usually water) mass
2299+ [substance] / [mass] -> [substance]: value * solvent_mass
2300+ [substance] -> [substance] / [mass]: value / solvent_mass
2301+
2302+ # moles/mass -> moles/volume require the solvent mass and the volume
2303+ [substance] / [mass] -> [substance]/[volume]: value * solvent_mass / volume
2304+ [substance] / [volume] -> [substance] / [mass]: value / solvent_mass * volume
2305+
2306+@end
2307
2308=== modified file 'pint/quantity.py'
2309--- pint/quantity.py 2014-09-05 23:26:05 +0000
2310+++ pint/quantity.py 2015-01-07 15:51:38 +0000
2311@@ -15,7 +15,8 @@
2312 import functools
2313
2314 from .formatting import remove_custom_flags
2315-from .unit import DimensionalityError, UnitsContainer, UnitDefinition, UndefinedUnitError
2316+from .unit import (DimensionalityError, OffsetUnitCalculusError,
2317+ UnitsContainer, UnitDefinition, UndefinedUnitError)
2318 from .compat import string_types, ndarray, np, _to_magnitude, long_type
2319 from .util import logger
2320
2321@@ -48,6 +49,8 @@
2322 # Both quantities are the same class and therefore from the same registry.
2323 # (Each registry has its own Quantity class)
2324 return True
2325+ elif q1._REGISTRY is getattr(other, '_REGISTRY', None):
2326+ return True
2327 elif isinstance(other, _Quantity):
2328 # The other object is a Quantity but from another registry.
2329 raise ValueError('Cannot operate between quantities of different registries')
2330@@ -55,21 +58,8 @@
2331 return False
2332
2333
2334-def _only_multiplicative_units(q):
2335- """Check if the quantity has non-multiplicative units.
2336- """
2337-
2338- # Compound units are never multiplicative
2339- if len(q.units) != 1:
2340- return True
2341-
2342- unit = list(q.units.keys())[0]
2343-
2344- return q._REGISTRY._units[unit].is_multiplicative
2345-
2346-
2347 class _Quantity(object):
2348- """Implements a class to describe a physical quantities:
2349+ """Implements a class to describe a physical quantity:
2350 the product of a numerical value and a unit of measurement.
2351
2352 :param value: value of the physical quantity to be created.
2353@@ -138,7 +128,7 @@
2354 spec = spec or self.default_format
2355
2356 if '~' in spec:
2357- units = UnitsContainer(dict((self._REGISTRY.get_symbol(key), value)
2358+ units = UnitsContainer(dict((self._REGISTRY._get_symbol(key), value)
2359 for key, value in self.units.items()))
2360 spec = spec.replace('~', '')
2361 else:
2362@@ -304,15 +294,8 @@
2363 :param op: operator function (e.g. operator.add, operator.isub)
2364 :type op: function
2365 """
2366- if _check(self, other):
2367- if not self.dimensionality == other.dimensionality:
2368- raise DimensionalityError(self.units, other.units,
2369- self.dimensionality, other.dimensionality)
2370- if self._units == other._units:
2371- self._magnitude = op(self._magnitude, other._magnitude)
2372- else:
2373- self._magnitude = op(self._magnitude, other.to(self)._magnitude)
2374- else:
2375+ if not _check(self, other):
2376+ # other not from same Registry or not a Quantity
2377 try:
2378 other_magnitude = _to_magnitude(other, self.force_ndarray)
2379 except TypeError:
2380@@ -328,6 +311,75 @@
2381 self._magnitude = op(self._magnitude, other_magnitude)
2382 else:
2383 raise DimensionalityError(self.units, 'dimensionless')
2384+ return self
2385+
2386+ if not self.dimensionality == other.dimensionality:
2387+ raise DimensionalityError(self.units, other.units,
2388+ self.dimensionality,
2389+ other.dimensionality)
2390+
2391+ # Next we define some variables to make if-clauses more readable.
2392+ self_non_mul_units = self._get_non_multiplicative_units()
2393+ is_self_multiplicative = len(self_non_mul_units) == 0
2394+ if len(self_non_mul_units) == 1:
2395+ self_non_mul_unit = self_non_mul_units[0]
2396+ other_non_mul_units = other._get_non_multiplicative_units()
2397+ is_other_multiplicative = len(other_non_mul_units) == 0
2398+ if len(other_non_mul_units) == 1:
2399+ other_non_mul_unit = other_non_mul_units[0]
2400+
2401+ # Presence of non-multiplicative units gives rise to several cases.
2402+ if is_self_multiplicative and is_other_multiplicative:
2403+ if self._units == other._units:
2404+ self._magnitude = op(self._magnitude, other._magnitude)
2405+ # If only self has a delta unit, other determines unit of result.
2406+ elif self._get_delta_units() and not other._get_delta_units():
2407+ self._magnitude = op(self._convert_magnitude(other.units),
2408+ other._magnitude)
2409+ self._units = copy.copy(other.units)
2410+ else:
2411+ self._magnitude = op(self._magnitude,
2412+ other.to(self.units)._magnitude)
2413+
2414+ elif (op == operator.isub and len(self_non_mul_units) == 1
2415+ and self.units[self_non_mul_unit] == 1
2416+ and not other._has_compatible_delta(self_non_mul_unit)):
2417+ if self.units == other.units:
2418+ self._magnitude = op(self._magnitude, other._magnitude)
2419+ else:
2420+ self._magnitude = op(self._magnitude,
2421+ other.to(self.units)._magnitude)
2422+ self.units['delta_' + self_non_mul_unit
2423+ ] = self.units.pop(self_non_mul_unit)
2424+
2425+ elif (op == operator.isub and len(other_non_mul_units) == 1
2426+ and other.units[other_non_mul_unit] == 1
2427+ and not self._has_compatible_delta(other_non_mul_unit)):
2428+ # we convert to self directly since it is multiplicative
2429+ self._magnitude = op(self._magnitude,
2430+ other.to(self.units)._magnitude)
2431+
2432+ elif (len(self_non_mul_units) == 1
2433+ # order of the dimension of offset unit == 1 ?
2434+ and self._units[self_non_mul_unit] == 1
2435+ and other._has_compatible_delta(self_non_mul_unit)):
2436+ tu = copy.copy(self.units)
2437+ # Replace offset unit in self by the corresponding delta unit.
2438+ # This is done to prevent a shift by offset in the to()-call.
2439+ tu['delta_' + self_non_mul_unit] = tu.pop(self_non_mul_unit)
2440+ self._magnitude = op(self._magnitude, other.to(tu)._magnitude)
2441+ elif (len(other_non_mul_units) == 1
2442+ # order of the dimension of offset unit == 1 ?
2443+ and other._units[other_non_mul_unit] == 1
2444+ and self._has_compatible_delta(other_non_mul_unit)):
2445+ tu = copy.copy(other.units)
2446+ # Replace offset unit in other by the corresponding delta unit.
2447+ # This is done to prevent a shift by offset in the to()-call.
2448+ tu['delta_' + other_non_mul_unit] = tu.pop(other_non_mul_unit)
2449+ self._magnitude = op(self._convert_magnitude(tu), other._magnitude)
2450+ self._units = copy.copy(other.units)
2451+ else:
2452+ raise OffsetUnitCalculusError(self.units, other.units)
2453
2454 return self
2455
2456@@ -339,17 +391,8 @@
2457 :param op: operator function (e.g. operator.add, operator.isub)
2458 :type op: function
2459 """
2460- if _check(self, other):
2461- if not self.dimensionality == other.dimensionality:
2462- raise DimensionalityError(self.units, other.units,
2463- self.dimensionality, other.dimensionality)
2464- if self._units == other._units:
2465- magnitude = op(self._magnitude, other._magnitude)
2466- else:
2467- magnitude = op(self._magnitude, other.to(self)._magnitude)
2468-
2469- units = copy.copy(self.units)
2470- else:
2471+ if not _check(self, other):
2472+ # other not from same Registry or not a Quantity
2473 if _eq(other, 0, True):
2474 # If the other value is 0 (but not Quantity 0)
2475 # do the operation without checking units.
2476@@ -364,6 +407,79 @@
2477 _to_magnitude(other, self.force_ndarray))
2478 else:
2479 raise DimensionalityError(self.units, 'dimensionless')
2480+ return self.__class__(magnitude, units)
2481+
2482+ if not self.dimensionality == other.dimensionality:
2483+ raise DimensionalityError(self.units, other.units,
2484+ self.dimensionality,
2485+ other.dimensionality)
2486+
2487+ # Next we define some variables to make if-clauses more readable.
2488+ self_non_mul_units = self._get_non_multiplicative_units()
2489+ is_self_multiplicative = len(self_non_mul_units) == 0
2490+ if len(self_non_mul_units) == 1:
2491+ self_non_mul_unit = self_non_mul_units[0]
2492+ other_non_mul_units = other._get_non_multiplicative_units()
2493+ is_other_multiplicative = len(other_non_mul_units) == 0
2494+ if len(other_non_mul_units) == 1:
2495+ other_non_mul_unit = other_non_mul_units[0]
2496+
2497+ # Presence of non-multiplicative units gives rise to several cases.
2498+ if is_self_multiplicative and is_other_multiplicative:
2499+ if self._units == other._units:
2500+ magnitude = op(self._magnitude, other._magnitude)
2501+ units = copy.copy(self.units)
2502+ # If only self has a delta unit, other determines unit of result.
2503+ elif self._get_delta_units() and not other._get_delta_units():
2504+ magnitude = op(self._convert_magnitude(other.units),
2505+ other._magnitude)
2506+ units = copy.copy(other.units)
2507+ else:
2508+ units = copy.copy(self.units)
2509+ magnitude = op(self._magnitude,
2510+ other.to(self.units).magnitude)
2511+
2512+ elif (op == operator.sub and len(self_non_mul_units) == 1
2513+ and self.units[self_non_mul_unit] == 1
2514+ and not other._has_compatible_delta(self_non_mul_unit)):
2515+ if self.units == other.units:
2516+ magnitude = op(self._magnitude, other._magnitude)
2517+ else:
2518+ magnitude = op(self._magnitude,
2519+ other.to(self.units)._magnitude)
2520+ units = copy.copy(self.units)
2521+ units['delta_' + self_non_mul_unit] = units.pop(self_non_mul_unit)
2522+
2523+ elif (op == operator.sub and len(other_non_mul_units) == 1
2524+ and other.units[other_non_mul_unit] == 1
2525+ and not self._has_compatible_delta(other_non_mul_unit)):
2526+ # we convert to self directly since it is multiplicative
2527+ magnitude = op(self._magnitude,
2528+ other.to(self.units)._magnitude)
2529+ units = copy.copy(self.units)
2530+
2531+ elif (len(self_non_mul_units) == 1
2532+ # order of the dimension of offset unit == 1 ?
2533+ and self._units[self_non_mul_unit] == 1
2534+ and other._has_compatible_delta(self_non_mul_unit)):
2535+ tu = copy.copy(self.units)
2536+ # Replace offset unit in self by the corresponding delta unit.
2537+ # This is done to prevent a shift by offset in the to()-call.
2538+ tu['delta_' + self_non_mul_unit] = tu.pop(self_non_mul_unit)
2539+ magnitude = op(self._magnitude, other.to(tu).magnitude)
2540+ units = copy.copy(self.units)
2541+ elif (len(other_non_mul_units) == 1
2542+ # order of the dimension of offset unit == 1 ?
2543+ and other._units[other_non_mul_unit] == 1
2544+ and self._has_compatible_delta(other_non_mul_unit)):
2545+ tu = copy.copy(other.units)
2546+ # Replace offset unit in other by the corresponding delta unit.
2547+ # This is done to prevent a shift by offset in the to()-call.
2548+ tu['delta_' + other_non_mul_unit] = tu.pop(other_non_mul_unit)
2549+ magnitude = op(self._convert_magnitude(tu), other._magnitude)
2550+ units = copy.copy(other.units)
2551+ else:
2552+ raise OffsetUnitCalculusError(self.units, other.units)
2553
2554 return self.__class__(magnitude, units)
2555
2556@@ -403,24 +519,40 @@
2557 if units_op is None:
2558 units_op = magnitude_op
2559
2560- if self.__used:
2561- if not _only_multiplicative_units(self):
2562- self.ito_base_units()
2563- else:
2564- self.__used = True
2565+ offset_units_self = self._get_non_multiplicative_units()
2566+ no_offset_units_self = len(offset_units_self)
2567
2568- if _check(self, other):
2569- if not _only_multiplicative_units(other):
2570- other = other.to_base_units()
2571- self._magnitude = magnitude_op(self._magnitude, other._magnitude)
2572- self._units = units_op(self._units, other._units)
2573- else:
2574+ if not _check(self, other):
2575+ if not self._ok_for_muldiv(no_offset_units_self):
2576+ raise OffsetUnitCalculusError(self.units,
2577+ getattr(other, 'units', ''))
2578+ if len(offset_units_self) == 1:
2579+ if (self.units[offset_units_self[0]] != 1
2580+ or magnitude_op not in [operator.mul, operator.imul]):
2581+ raise OffsetUnitCalculusError(self.units,
2582+ getattr(other, 'units', ''))
2583 try:
2584 other_magnitude = _to_magnitude(other, self.force_ndarray)
2585 except TypeError:
2586 return NotImplemented
2587 self._magnitude = magnitude_op(self._magnitude, other_magnitude)
2588 self._units = units_op(self._units, UnitsContainer())
2589+ return self
2590+
2591+ if not self._ok_for_muldiv(no_offset_units_self):
2592+ raise OffsetUnitCalculusError(self.units, other.units)
2593+ elif no_offset_units_self == 1 and len(self.units) == 1:
2594+ self.ito_base_units()
2595+
2596+ no_offset_units_other = len(other._get_non_multiplicative_units())
2597+
2598+ if not other._ok_for_muldiv(no_offset_units_other):
2599+ raise OffsetUnitCalculusError(self.units, other.units)
2600+ elif no_offset_units_other == 1 and len(other.units) == 1:
2601+ other.ito_base_units()
2602+
2603+ self._magnitude = magnitude_op(self._magnitude, other._magnitude)
2604+ self._units = units_op(self._units, other._units)
2605
2606 return self
2607
2608@@ -437,27 +569,46 @@
2609 if units_op is None:
2610 units_op = magnitude_op
2611
2612+ offset_units_self = self._get_non_multiplicative_units()
2613+ no_offset_units_self = len(offset_units_self)
2614+
2615+ if not _check(self, other):
2616+ if not self._ok_for_muldiv(no_offset_units_self):
2617+ raise OffsetUnitCalculusError(self.units,
2618+ getattr(other, 'units', ''))
2619+ if len(offset_units_self) == 1:
2620+ if (self.units[offset_units_self[0]] != 1
2621+ or magnitude_op not in [operator.mul, operator.imul]):
2622+ raise OffsetUnitCalculusError(self.units,
2623+ getattr(other, 'units', ''))
2624+ try:
2625+ other_magnitude = _to_magnitude(other, self.force_ndarray)
2626+ except TypeError:
2627+ return NotImplemented
2628+
2629+ magnitude = magnitude_op(self._magnitude, other_magnitude)
2630+ units = units_op(self._units, UnitsContainer())
2631+
2632+ return self.__class__(magnitude, units)
2633+
2634 new_self = self
2635- if self.__used:
2636- if not _only_multiplicative_units(self):
2637- new_self = self.to_base_units()
2638-
2639- if _check(self, other):
2640- if not _only_multiplicative_units(other):
2641- other = other.to_base_units()
2642- magnitude = magnitude_op(new_self._magnitude, other._magnitude)
2643- units = units_op(new_self._units, other._units)
2644- else:
2645- try:
2646- other_magnitude = _to_magnitude(other, self.force_ndarray)
2647- except TypeError:
2648- return NotImplemented
2649- magnitude = magnitude_op(new_self._magnitude, other_magnitude)
2650- units = units_op(new_self._units, UnitsContainer())
2651-
2652- ret = self.__class__(magnitude, units)
2653- ret.__used = True
2654- return ret
2655+
2656+ if not self._ok_for_muldiv(no_offset_units_self):
2657+ raise OffsetUnitCalculusError(self.units, other.units)
2658+ elif no_offset_units_self == 1 and len(self.units) == 1:
2659+ new_self = self.to_base_units()
2660+
2661+ no_offset_units_other = len(other._get_non_multiplicative_units())
2662+
2663+ if not other._ok_for_muldiv(no_offset_units_other):
2664+ raise OffsetUnitCalculusError(self.units, other.units)
2665+ elif no_offset_units_other == 1 and len(other.units) == 1:
2666+ other = other.to_base_units()
2667+
2668+ magnitude = magnitude_op(new_self._magnitude, other._magnitude)
2669+ units = units_op(new_self._units, other._units)
2670+
2671+ return self.__class__(magnitude, units)
2672
2673 def __imul__(self, other):
2674 if not isinstance(self._magnitude, ndarray):
2675@@ -493,6 +644,13 @@
2676 other_magnitude = _to_magnitude(other, self.force_ndarray)
2677 except TypeError:
2678 return NotImplemented
2679+
2680+ no_offset_units_self = len(self._get_non_multiplicative_units())
2681+ if not self._ok_for_muldiv(no_offset_units_self):
2682+ raise OffsetUnitCalculusError(self.units, '')
2683+ elif no_offset_units_self == 1 and len(self.units) == 1:
2684+ self = self.to_base_units()
2685+
2686 return self.__class__(other_magnitude / self._magnitude, 1 / self._units)
2687
2688 def __rfloordiv__(self, other):
2689@@ -500,6 +658,13 @@
2690 other_magnitude = _to_magnitude(other, self.force_ndarray)
2691 except TypeError:
2692 return NotImplemented
2693+
2694+ no_offset_units_self = len(self._get_non_multiplicative_units())
2695+ if not self._ok_for_muldiv(no_offset_units_self):
2696+ raise OffsetUnitCalculusError(self.units, '')
2697+ elif no_offset_units_self == 1 and len(self.units) == 1:
2698+ self = self.to_base_units()
2699+
2700 return self.__class__(other_magnitude // self._magnitude, 1 / self._units)
2701
2702 __div__ = __truediv__
2703@@ -515,10 +680,36 @@
2704 except TypeError:
2705 return NotImplemented
2706 else:
2707- if not _only_multiplicative_units(self):
2708- self.ito_base_units()
2709+ if not self._ok_for_muldiv:
2710+ raise OffsetUnitCalculusError(self.units)
2711+
2712+ if isinstance(getattr(other, '_magnitude', other), ndarray):
2713+ # arrays are refused as exponent, because they would create
2714+ # len(array) quanitites of len(set(array)) different units
2715+ if np.size(other) > 1:
2716+ raise DimensionalityError(self.units, 'dimensionless')
2717+
2718+ new_self = self
2719+ if other == 1:
2720+ return self
2721+ elif other == 0:
2722+ self._units = UnitsContainer()
2723+ else:
2724+ if not self._is_multiplicative:
2725+ if self._REGISTRY.autoconvert_offset_to_baseunit:
2726+ self.ito_base_units()
2727+ else:
2728+ raise OffsetUnitCalculusError(self.units)
2729+
2730+ if getattr(other, 'dimensionless', False):
2731+ other = other.to_base_units()
2732+ self._units **= other.magnitude
2733+ elif not getattr(other, 'dimensionless', True):
2734+ raise DimensionalityError(self.units, 'dimensionless')
2735+ else:
2736+ self._units **= other
2737+
2738 self._magnitude **= _to_magnitude(other, self.force_ndarray)
2739- self._units **= other
2740 return self
2741
2742 def __pow__(self, other):
2743@@ -527,14 +718,51 @@
2744 except TypeError:
2745 return NotImplemented
2746 else:
2747+ if not self._ok_for_muldiv:
2748+ raise OffsetUnitCalculusError(self.units)
2749+
2750+ if isinstance(getattr(other, '_magnitude', other), ndarray):
2751+ # arrays are refused as exponent, because they would create
2752+ # len(array) quantities of len(set(array)) different units
2753+ if np.size(other) > 1:
2754+ raise DimensionalityError(self.units, 'dimensionless')
2755+
2756 new_self = self
2757- if not _only_multiplicative_units(self):
2758- new_self = self.to_base_units()
2759+ if other == 1:
2760+ return self
2761+ elif other == 0:
2762+ units = UnitsContainer()
2763+ else:
2764+ if not self._is_multiplicative:
2765+ if self._REGISTRY.autoconvert_offset_to_baseunit:
2766+ new_self = self.to_base_units()
2767+ else:
2768+ raise OffsetUnitCalculusError(self.units)
2769+
2770+ if getattr(other, 'dimensionless', False):
2771+ units = new_self._units ** other.to_base_units().magnitude
2772+ elif not getattr(other, 'dimensionless', True):
2773+ raise DimensionalityError(self.units, 'dimensionless')
2774+ else:
2775+ units = new_self._units ** other
2776
2777 magnitude = new_self._magnitude ** _to_magnitude(other, self.force_ndarray)
2778- units = new_self._units ** other
2779 return self.__class__(magnitude, units)
2780
2781+ def __rpow__(self, other):
2782+ try:
2783+ other_magnitude = _to_magnitude(other, self.force_ndarray)
2784+ except TypeError:
2785+ return NotImplemented
2786+ else:
2787+ if not self.dimensionless:
2788+ raise DimensionalityError(self.units, 'dimensionless')
2789+ if isinstance(self._magnitude, ndarray):
2790+ if np.size(self._magnitude) > 1:
2791+ raise DimensionalityError(self.units, 'dimensionless')
2792+ new_self = self.to_base_units()
2793+ return other**new_self._magnitude
2794+
2795 def __abs__(self):
2796 return self.__class__(abs(self._magnitude), self._units)
2797
2798@@ -798,7 +1026,10 @@
2799
2800 if isinstance(factor, self.__class__):
2801 if not factor.dimensionless:
2802- raise ValueError
2803+ raise DimensionalityError(value, self.units,
2804+ extra_msg='. Assign a quantity with the same dimensionality or '
2805+ 'access the magnitude directly as '
2806+ '`obj.magnitude[%s] = %s`' % (key, value))
2807 self._magnitude[key] = factor.magnitude
2808 else:
2809 self._magnitude[key] = factor
2810@@ -961,3 +1192,58 @@
2811 error = error * abs(self.magnitude)
2812
2813 return self._REGISTRY.Measurement(copy.copy(self.magnitude), error, self.units)
2814+
2815+ # methods/properties that help for math operations with offset units
2816+ @property
2817+ def _is_multiplicative(self):
2818+ """Check if the Quantity object has only multiplicative units.
2819+ """
2820+ # XXX Turn this into a method/property of _Quantity?
2821+ return not self._get_non_multiplicative_units()
2822+
2823+ def _get_non_multiplicative_units(self):
2824+ """Return a list of the of non-multiplicative units of the Quantity object
2825+ """
2826+ offset_units = [unit for unit in self.units.keys()
2827+ if not self._REGISTRY._units[unit].is_multiplicative]
2828+ return offset_units
2829+
2830+ def _get_delta_units(self):
2831+ """Return list of delta units ot the Quantity object
2832+ """
2833+ delta_units = [u for u in self.units.keys() if u.startswith("delta_")]
2834+ return delta_units
2835+
2836+ def _has_compatible_delta(self, unit):
2837+ """"Check if Quantity object has a delta_unit that is compatible with unit
2838+ """
2839+ deltas = self._get_delta_units()
2840+ if 'delta_' + unit in deltas:
2841+ return True
2842+ else: # Look for delta units with same dimension as the offset unit
2843+ offset_unit_dim = self._REGISTRY._units[unit].reference
2844+ for d in deltas:
2845+ if self._REGISTRY._units[d].reference == offset_unit_dim:
2846+ return True
2847+ return False
2848+
2849+ def _ok_for_muldiv(self, no_offset_units=None):
2850+ """Checks if Quantity object can be multiplied or divided
2851+
2852+ :q: quantity object that is checked
2853+ :no_offset_units: number of offset units in q
2854+ """
2855+ is_ok = True
2856+ if no_offset_units is None:
2857+ no_offset_units = len(self._get_non_multiplicative_units())
2858+ if no_offset_units > 1:
2859+ is_ok = False
2860+ if no_offset_units == 1:
2861+ if len(self._units) > 1:
2862+ is_ok = False
2863+ if (len(self._units) == 1
2864+ and not self._REGISTRY.autoconvert_offset_to_baseunit):
2865+ is_ok = False
2866+ if next(iter(self._units.values())) != 1:
2867+ is_ok = False
2868+ return is_ok
2869
2870=== added file 'pint/testsuite/parameterized.py'
2871--- pint/testsuite/parameterized.py 1970-01-01 00:00:00 +0000
2872+++ pint/testsuite/parameterized.py 2015-01-07 15:51:38 +0000
2873@@ -0,0 +1,152 @@
2874+# -*- coding: utf-8 -*-
2875+#
2876+# Adds Parameterized tests for Python's unittest module
2877+#
2878+# Code from: parameterizedtestcase, version: 0.1.0
2879+# Homepage: https://github.com/msabramo/python_unittest_parameterized_test_case
2880+# Author: Marc Abramowitz, email: marc@marc-abramowitz.com
2881+# License: MIT
2882+#
2883+# Fixed for to work in Python 2 & 3 with "add_metaclass" decorator from six
2884+# https://pypi.python.org/pypi/six
2885+# Author: Benjamin Peterson
2886+# License: MIT
2887+#
2888+# Use like this:
2889+#
2890+# from parameterizedtestcase import ParameterizedTestCase
2891+#
2892+# class MyTests(ParameterizedTestCase):
2893+# @ParameterizedTestCase.parameterize(
2894+# ("input", "expected_output"),
2895+# [
2896+# ("2+4", 6),
2897+# ("3+5", 8),
2898+# ("6*9", 54),
2899+# ]
2900+# )
2901+# def test_eval(self, input, expected_output):
2902+# self.assertEqual(eval(input), expected_output)
2903+
2904+try:
2905+ import unittest2 as unittest
2906+except ImportError: # pragma: no cover
2907+ import unittest
2908+
2909+from functools import wraps
2910+import collections
2911+
2912+
2913+def add_metaclass(metaclass):
2914+ """Class decorator for creating a class with a metaclass."""
2915+ def wrapper(cls):
2916+ orig_vars = cls.__dict__.copy()
2917+ orig_vars.pop('__dict__', None)
2918+ orig_vars.pop('__weakref__', None)
2919+ slots = orig_vars.get('__slots__')
2920+ if slots is not None:
2921+ if isinstance(slots, str):
2922+ slots = [slots]
2923+ for slots_var in slots:
2924+ orig_vars.pop(slots_var)
2925+ return metaclass(cls.__name__, cls.__bases__, orig_vars)
2926+ return wrapper
2927+
2928+
2929+def augment_method_docstring(method, new_class_dict, classname,
2930+ param_names, param_values, new_method):
2931+ param_assignments_str = '; '.join(
2932+ ['%s = %s' % (k, v) for (k, v) in zip(param_names, param_values)])
2933+ extra_doc = "%s (%s.%s) [with %s] " % (
2934+ method.__name__, new_class_dict.get('__module__', '<module>'),
2935+ classname, param_assignments_str)
2936+
2937+ try:
2938+ new_method.__doc__ = extra_doc + new_method.__doc__
2939+ except TypeError: # Catches when new_method.__doc__ is None
2940+ new_method.__doc__ = extra_doc
2941+
2942+
2943+class ParameterizedTestCaseMetaClass(type):
2944+ method_counter = {}
2945+
2946+ def __new__(meta, classname, bases, class_dict):
2947+ new_class_dict = {}
2948+
2949+ for attr_name, attr_value in list(class_dict.items()):
2950+ if isinstance(attr_value, collections.Callable) and hasattr(attr_value, 'param_names'):
2951+ # print("Processing attr_name = %r; attr_value = %r" % (
2952+ # attr_name, attr_value))
2953+
2954+ method = attr_value
2955+ param_names = attr_value.param_names
2956+ data = attr_value.data
2957+ func_name_format = attr_value.func_name_format
2958+
2959+ meta.process_method(
2960+ classname, method, param_names, data, new_class_dict,
2961+ func_name_format)
2962+ else:
2963+ new_class_dict[attr_name] = attr_value
2964+
2965+ return type.__new__(meta, classname, bases, new_class_dict)
2966+
2967+ @classmethod
2968+ def process_method(
2969+ cls, classname, method, param_names, data, new_class_dict,
2970+ func_name_format):
2971+ method_counter = cls.method_counter
2972+
2973+ for param_values in data:
2974+ new_method = cls.new_method(method, param_values)
2975+ method_counter[method.__name__] = \
2976+ method_counter.get(method.__name__, 0) + 1
2977+ case_data = dict(list(zip(param_names, param_values)))
2978+ case_data['func_name'] = method.__name__
2979+ case_data['case_num'] = method_counter[method.__name__]
2980+
2981+ new_method.__name__ = func_name_format.format(**case_data)
2982+
2983+ augment_method_docstring(
2984+ method, new_class_dict, classname,
2985+ param_names, param_values, new_method)
2986+ new_class_dict[new_method.__name__] = new_method
2987+
2988+ @classmethod
2989+ def new_method(cls, method, param_values):
2990+ @wraps(method)
2991+ def new_method(self):
2992+ return method(self, *param_values)
2993+
2994+ return new_method
2995+
2996+@add_metaclass(ParameterizedTestCaseMetaClass)
2997+class ParameterizedTestMixin(object):
2998+ @classmethod
2999+ def parameterize(cls, param_names, data,
3000+ func_name_format='{func_name}_{case_num:05d}'):
3001+ """Decorator for parameterizing a test method - example:
3002+
3003+ @ParameterizedTestCase.parameterize(
3004+ ("isbn", "expected_title"), [
3005+ ("0262033844", "Introduction to Algorithms"),
3006+ ("0321558146", "Campbell Essential Biology")])
3007+
3008+ """
3009+
3010+ def decorator(func):
3011+ @wraps(func)
3012+ def newfunc(*arg, **kwargs):
3013+ return func(*arg, **kwargs)
3014+
3015+ newfunc.param_names = param_names
3016+ newfunc.data = data
3017+ newfunc.func_name_format = func_name_format
3018+
3019+ return newfunc
3020+
3021+ return decorator
3022+
3023+@add_metaclass(ParameterizedTestCaseMetaClass)
3024+class ParameterizedTestCase(unittest.TestCase, ParameterizedTestMixin):
3025+ pass
3026
3027=== modified file 'pint/testsuite/test_issues.py'
3028--- pint/testsuite/test_issues.py 2014-09-05 23:26:05 +0000
3029+++ pint/testsuite/test_issues.py 2015-01-07 15:51:38 +0000
3030@@ -16,6 +16,9 @@
3031
3032 FORCE_NDARRAY = False
3033
3034+ def setup(self):
3035+ self.ureg.autoconvert_offset_to_baseunit = False
3036+
3037 @unittest.expectedFailure
3038 def test_issue25(self):
3039 x = ParserHelper.from_string('10 %')
3040@@ -92,9 +95,9 @@
3041 def test_issue66b(self):
3042 ureg = UnitRegistry()
3043 self.assertEqual(ureg.get_base_units(ureg.kelvin.units),
3044- (None, UnitsContainer({'kelvin': 1})))
3045+ (1.0, UnitsContainer({'kelvin': 1})))
3046 self.assertEqual(ureg.get_base_units(ureg.degC.units),
3047- (None, UnitsContainer({'kelvin': 1})))
3048+ (1.0, UnitsContainer({'kelvin': 1})))
3049
3050 def test_issue69(self):
3051 ureg = UnitRegistry()
3052@@ -127,11 +130,12 @@
3053 self.assertQuantityAlmostEqual(va.to_base_units(), vb.to_base_units())
3054
3055 def test_issue86(self):
3056+ ureg = self.ureg
3057+ ureg.autoconvert_offset_to_baseunit = True
3058+
3059 def parts(q):
3060 return q.magnitude, q.units
3061
3062- ureg = UnitRegistry()
3063-
3064 q1 = 10. * ureg.degC
3065 q2 = 10. * ureg.kelvin
3066
3067@@ -158,7 +162,6 @@
3068 self.assertEqual(parts(q1 / q3), (k1m / q3m, k1u / q3u))
3069 self.assertEqual(parts(q3 * q1), (q3m * k1m, q3u * k1u))
3070 self.assertEqual(parts(q3 / q1), (q3m / k1m, q3u / k1u))
3071- self.assertEqual(parts(q1 ** 1), (k1m ** 1, k1u ** 1))
3072 self.assertEqual(parts(q1 ** -1), (k1m ** -1, k1u ** -1))
3073 self.assertEqual(parts(q1 ** 2), (k1m ** 2, k1u ** 2))
3074 self.assertEqual(parts(q1 ** -2), (k1m ** -2, k1u ** -2))
3075@@ -177,8 +180,10 @@
3076 self.assertQuantityAlmostEqual(v1.to_base_units(), v2)
3077 self.assertQuantityAlmostEqual(v1.to_base_units(), v2.to_base_units())
3078
3079+ @unittest.expectedFailure
3080 def test_issue86c(self):
3081 ureg = self.ureg
3082+ ureg.autoconvert_offset_to_baseunit = True
3083 T = ureg.degC
3084 T = 100. * T
3085 self.assertQuantityAlmostEqual(ureg.k*2*T, ureg.k*(2*T))
3086
3087=== modified file 'pint/testsuite/test_numpy.py'
3088--- pint/testsuite/test_numpy.py 2014-09-05 23:26:05 +0000
3089+++ pint/testsuite/test_numpy.py 2015-01-07 15:51:38 +0000
3090@@ -2,8 +2,13 @@
3091
3092 from __future__ import division, unicode_literals, print_function, absolute_import
3093
3094+import copy
3095+import operator as op
3096+
3097+from pint import DimensionalityError
3098 from pint.compat import np, unittest
3099 from pint.testsuite import QuantityTestCase, helpers
3100+from pint.testsuite.test_umath import TestUFuncs
3101
3102
3103 @helpers.requires_numpy()
3104@@ -189,9 +194,11 @@
3105 q[0] = 1*self.ureg.m
3106 self.assertQuantityEqual(q, [[1,1],[3,4]]*self.ureg.m)
3107
3108- q[0] = (1,2)*self.ureg.m
3109- self.assertQuantityEqual(q, self.q)
3110+ q = self.q.copy()
3111+ q.__setitem__(Ellipsis, 1*self.ureg.m)
3112+ self.assertQuantityEqual(q, [[1,1],[1,1]]*self.ureg.m)
3113
3114+ q = self.q.copy()
3115 q[:] = 1*self.ureg.m
3116 self.assertQuantityEqual(q, [[1,1],[1,1]]*self.ureg.m)
3117
3118@@ -239,58 +246,68 @@
3119 self.assertQuantityEqual(u == 1, u.magnitude == 1)
3120
3121
3122-from pint.testsuite.test_umath import TestUFuncs
3123-@unittest.skip
3124-class TestNumpyNotSupported(TestUFuncs):
3125+@helpers.requires_numpy()
3126+class TestNumpyNeedsSubclassing(TestUFuncs):
3127
3128 FORCE_NDARRAY = True
3129
3130+ @property
3131+ def q(self):
3132+ return [1. ,2., 3., 4.] * self.ureg.J
3133
3134+ @unittest.expectedFailure
3135 def test_unwrap(self):
3136 """unwrap depends on diff
3137 """
3138- self.assertEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi])
3139- self.assertEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg)
3140+ self.assertQuantityEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi])
3141+ self.assertQuantityEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg)
3142
3143+ @unittest.expectedFailure
3144 def test_trapz(self):
3145 """Units are erased by asanyarray, Quantity does not inherit from NDArray
3146 """
3147- self.assertEqual(np.trapz(self.q, dx = 1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m)
3148+ self.assertQuantityEqual(np.trapz(self.q, dx=1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m)
3149
3150+ @unittest.expectedFailure
3151 def test_diff(self):
3152 """Units are erased by asanyarray, Quantity does not inherit from NDArray
3153 """
3154 self.assertQuantityEqual(np.diff(self.q, 1), [1, 1, 1] * self.ureg.J)
3155
3156+ @unittest.expectedFailure
3157 def test_ediff1d(self):
3158 """Units are erased by asanyarray, Quantity does not inherit from NDArray
3159 """
3160- self.assertEqual(np.ediff1d(self.q, 1), [1, 1, 1] * self.ureg.J)
3161+ self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.J), [1, 1, 1] * self.ureg.J)
3162
3163+ @unittest.expectedFailure
3164 def test_fix(self):
3165 """Units are erased by asanyarray, Quantity does not inherit from NDArray
3166 """
3167- self.assertEqual(np.fix(3.14 * self.ureg.m), 3.0 * self.ureg.m)
3168- self.assertEqual(np.fix(3.0 * self.ureg.m), 3.0 * self.ureg.m)
3169+ self.assertQuantityEqual(np.fix(3.14 * self.ureg.m), 3.0 * self.ureg.m)
3170+ self.assertQuantityEqual(np.fix(3.0 * self.ureg.m), 3.0 * self.ureg.m)
3171 self.assertQuantityEqual(
3172 np.fix([2.1, 2.9, -2.1, -2.9] * self.ureg.m),
3173 [2., 2., -2., -2.] * self.ureg.m
3174 )
3175
3176+ @unittest.expectedFailure
3177 def test_gradient(self):
3178 """shape is a property not a function
3179 """
3180 l = np.gradient([[1,1],[3,4]] * self.ureg.J, 1 * self.ureg.m)
3181- self.assertEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.J / self.ureg.m)
3182- self.assertEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.J / self.ureg.m)
3183+ self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.J / self.ureg.m)
3184+ self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.J / self.ureg.m)
3185
3186+ @unittest.expectedFailure
3187 def test_cross(self):
3188 """Units are erased by asarray, Quantity does not inherit from NDArray
3189 """
3190- a = [3,-3, 1] * self.ureg.kPa
3191- b = [4, 9, 2] * self.ureg.m**2
3192- self.assertQuantityEqual(np.cross(a,b), [-15,-2,39]*self.ureg.kPa*self.ureg.m**2)
3193+ a = [[3,-3, 1]] * self.ureg.kPa
3194+ b = [[4, 9, 2]] * self.ureg.m**2
3195+ self.assertQuantityEqual(np.cross(a, b), [-15, -2, 39] * self.ureg.kPa * self.ureg.m**2)
3196
3197+ @unittest.expectedFailure
3198 def test_power(self):
3199 """This is not supported as different elements might end up with different units
3200
3201@@ -302,6 +319,7 @@
3202 (self.qless, np.asarray([1., 2, 3, 4])),
3203 (self.q2, ),)
3204
3205+ @unittest.expectedFailure
3206 def test_ones_like(self):
3207 """Units are erased by emptyarra, Quantity does not inherit from NDArray
3208 """
3209@@ -385,3 +403,39 @@
3210 (self.qless, 2),
3211 (self.q1, self.q2, self.qs, ),
3212 'same')
3213+
3214+
3215+class TestNDArrayQunatityMath(QuantityTestCase):
3216+
3217+ @helpers.requires_numpy()
3218+ def test_exponentiation_array_exp(self):
3219+ arr = np.array(range(3), dtype=np.float)
3220+ q = self.Q_(arr, None)
3221+
3222+ for op_ in [op.pow, op.ipow]:
3223+ q_cp = copy.copy(q)
3224+ self.assertRaises(DimensionalityError, op_, 2., q_cp)
3225+ arr_cp = copy.copy(arr)
3226+ arr_cp = copy.copy(arr)
3227+ q_cp = copy.copy(q)
3228+ self.assertRaises(DimensionalityError, op_, q_cp, arr_cp)
3229+ q_cp = copy.copy(q)
3230+ q2_cp = copy.copy(q)
3231+ self.assertRaises(DimensionalityError, op_, q_cp, q2_cp)
3232+
3233+ @unittest.expectedFailure
3234+ @helpers.requires_numpy()
3235+ def test_exponentiation_array_exp_2(self):
3236+ arr = np.array(range(3), dtype=np.float)
3237+ #q = self.Q_(copy.copy(arr), None)
3238+ q = self.Q_(copy.copy(arr), 'meter')
3239+ arr_cp = copy.copy(arr)
3240+ q_cp = copy.copy(q)
3241+ # this fails as expected since numpy 1.8.0 but...
3242+ self.assertRaises(DimensionalityError, op.pow, arr_cp, q_cp)
3243+ # ..not for op.ipow !
3244+ # q_cp is treated as if it is an array. The units are ignored.
3245+ # _Quantity.__ipow__ is never called
3246+ arr_cp = copy.copy(arr)
3247+ q_cp = copy.copy(q)
3248+ self.assertRaises(DimensionalityError, op.ipow, arr_cp, q_cp)
3249
3250=== modified file 'pint/testsuite/test_quantity.py'
3251--- pint/testsuite/test_quantity.py 2014-09-05 23:26:05 +0000
3252+++ pint/testsuite/test_quantity.py 2015-01-07 15:51:38 +0000
3253@@ -6,10 +6,11 @@
3254 import math
3255 import operator as op
3256
3257-from pint import DimensionalityError, UnitRegistry
3258+from pint import DimensionalityError, OffsetUnitCalculusError, UnitRegistry
3259 from pint.unit import UnitsContainer
3260-from pint.compat import string_types, PYTHON3, np
3261+from pint.compat import string_types, PYTHON3, np, unittest
3262 from pint.testsuite import QuantityTestCase, helpers
3263+from pint.testsuite.parameterized import ParameterizedTestCase
3264
3265
3266 class TestQuantity(QuantityTestCase):
3267@@ -149,7 +150,7 @@
3268 # Conversions with single units take a different codepath than
3269 # Conversions with more than one unit.
3270 src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1)
3271- src_dst2 = UnitsContainer(meter=1, seconds=-1), UnitsContainer(inch=1, minutes=-1)
3272+ src_dst2 = UnitsContainer(meter=1, second=-1), UnitsContainer(inch=1, minute=-1)
3273 for src, dst in (src_dst1, src_dst2):
3274 a = np.ones((3, 1))
3275 ac = np.ones((3, 1))
3276@@ -209,14 +210,12 @@
3277
3278
3279 def test_offset_delta(self):
3280- self.assertQuantityAlmostEqual(self.Q_(0, 'delta_kelvin').to('delta_kelvin'), self.Q_(0, 'delta_kelvin'))
3281- self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degC').to('delta_kelvin'), self.Q_(0, 'delta_kelvin'))
3282- self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degF').to('delta_kelvin'), self.Q_(0, 'delta_kelvin'), rtol=0.01)
3283+ self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degC').to('kelvin'), self.Q_(0, 'kelvin'))
3284+ self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degF').to('kelvin'), self.Q_(0, 'kelvin'), rtol=0.01)
3285
3286- self.assertQuantityAlmostEqual(self.Q_(100, 'delta_kelvin').to('delta_kelvin'), self.Q_(100, 'delta_kelvin'))
3287- self.assertQuantityAlmostEqual(self.Q_(100, 'delta_kelvin').to('delta_degC'), self.Q_(100, 'delta_degC'))
3288- self.assertQuantityAlmostEqual(self.Q_(100, 'delta_kelvin').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01)
3289- self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('delta_kelvin'), self.Q_(55.55555556, 'delta_kelvin'), rtol=0.01)
3290+ self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('delta_degC'), self.Q_(100, 'delta_degC'))
3291+ self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01)
3292+ self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('kelvin'), self.Q_(55.55555556, 'kelvin'), rtol=0.01)
3293 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degC').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01)
3294 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('delta_degC'), self.Q_(55.55555556, 'delta_degC'), rtol=0.01)
3295
3296@@ -450,3 +449,547 @@
3297 from pint import _DEFAULT_REGISTRY
3298 cls.ureg = _DEFAULT_REGISTRY
3299 cls.Q_ = cls.ureg.Quantity
3300+
3301+
3302+class TestOffsetUnitMath(QuantityTestCase, ParameterizedTestCase):
3303+
3304+ def setup(self):
3305+ self.ureg.autoconvert_offset_to_baseunit = False
3306+ self.ureg.default_as_delta = True
3307+
3308+ additions = [
3309+ # --- input tuple -------------------- | -- expected result --
3310+ (((100, 'kelvin'), (10, 'kelvin')), (110, 'kelvin')),
3311+ (((100, 'kelvin'), (10, 'degC')), 'error'),
3312+ (((100, 'kelvin'), (10, 'degF')), 'error'),
3313+ (((100, 'kelvin'), (10, 'degR')), (105.56, 'kelvin')),
3314+ (((100, 'kelvin'), (10, 'delta_degC')), (110, 'kelvin')),
3315+ (((100, 'kelvin'), (10, 'delta_degF')), (105.56, 'kelvin')),
3316+
3317+ (((100, 'degC'), (10, 'kelvin')), 'error'),
3318+ (((100, 'degC'), (10, 'degC')), 'error'),
3319+ (((100, 'degC'), (10, 'degF')), 'error'),
3320+ (((100, 'degC'), (10, 'degR')), 'error'),
3321+ (((100, 'degC'), (10, 'delta_degC')), (110, 'degC')),
3322+ (((100, 'degC'), (10, 'delta_degF')), (105.56, 'degC')),
3323+
3324+ (((100, 'degF'), (10, 'kelvin')), 'error'),
3325+ (((100, 'degF'), (10, 'degC')), 'error'),
3326+ (((100, 'degF'), (10, 'degF')), 'error'),
3327+ (((100, 'degF'), (10, 'degR')), 'error'),
3328+ (((100, 'degF'), (10, 'delta_degC')), (118, 'degF')),
3329+ (((100, 'degF'), (10, 'delta_degF')), (110, 'degF')),
3330+
3331+ (((100, 'degR'), (10, 'kelvin')), (118, 'degR')),
3332+ (((100, 'degR'), (10, 'degC')), 'error'),
3333+ (((100, 'degR'), (10, 'degF')), 'error'),
3334+ (((100, 'degR'), (10, 'degR')), (110, 'degR')),
3335+ (((100, 'degR'), (10, 'delta_degC')), (118, 'degR')),
3336+ (((100, 'degR'), (10, 'delta_degF')), (110, 'degR')),
3337+
3338+ (((100, 'delta_degC'), (10, 'kelvin')), (110, 'kelvin')),
3339+ (((100, 'delta_degC'), (10, 'degC')), (110, 'degC')),
3340+ (((100, 'delta_degC'), (10, 'degF')), (190, 'degF')),
3341+ (((100, 'delta_degC'), (10, 'degR')), (190, 'degR')),
3342+ (((100, 'delta_degC'), (10, 'delta_degC')), (110, 'delta_degC')),
3343+ (((100, 'delta_degC'), (10, 'delta_degF')), (105.56, 'delta_degC')),
3344+
3345+ (((100, 'delta_degF'), (10, 'kelvin')), (65.56, 'kelvin')),
3346+ (((100, 'delta_degF'), (10, 'degC')), (65.56, 'degC')),
3347+ (((100, 'delta_degF'), (10, 'degF')), (110, 'degF')),
3348+ (((100, 'delta_degF'), (10, 'degR')), (110, 'degR')),
3349+ (((100, 'delta_degF'), (10, 'delta_degC')), (118, 'delta_degF')),
3350+ (((100, 'delta_degF'), (10, 'delta_degF')), (110, 'delta_degF')),
3351+ ]
3352+
3353+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
3354+ additions)
3355+ def test_addition(self, input_tuple, expected):
3356+ self.ureg.autoconvert_offset_to_baseunit = False
3357+ qin1, qin2 = input_tuple
3358+ q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
3359+ # update input tuple with new values to have correct values on failure
3360+ input_tuple = q1, q2
3361+ if expected == 'error':
3362+ self.assertRaises(OffsetUnitCalculusError, op.add, q1, q2)
3363+ else:
3364+ expected = self.Q_(*expected)
3365+ self.assertEqual(op.add(q1, q2).units, expected.units)
3366+ self.assertQuantityAlmostEqual(op.add(q1, q2), expected,
3367+ atol=0.01)
3368+
3369+ @helpers.requires_numpy()
3370+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
3371+ additions)
3372+ def test_inplace_addition(self, input_tuple, expected):
3373+ self.ureg.autoconvert_offset_to_baseunit = False
3374+ (q1v, q1u), (q2v, q2u) = input_tuple
3375+ # update input tuple with new values to have correct values on failure
3376+ input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
3377+ (np.array([q2v]*2, dtype=np.float), q2u))
3378+ Q_ = self.Q_
3379+ qin1, qin2 = input_tuple
3380+ q1, q2 = Q_(*qin1), Q_(*qin2)
3381+ q1_cp = copy.copy(q1)
3382+ if expected == 'error':
3383+ self.assertRaises(OffsetUnitCalculusError, op.iadd, q1_cp, q2)
3384+ else:
3385+ expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
3386+ self.assertEqual(op.iadd(q1_cp, q2).units, Q_(*expected).units)
3387+ q1_cp = copy.copy(q1)
3388+ self.assertQuantityAlmostEqual(op.iadd(q1_cp, q2), Q_(*expected),
3389+ atol=0.01)
3390+
3391+ subtractions = [
3392+ (((100, 'kelvin'), (10, 'kelvin')), (90, 'kelvin')),
3393+ (((100, 'kelvin'), (10, 'degC')), (-183.15, 'kelvin')),
3394+ (((100, 'kelvin'), (10, 'degF')), (-160.93, 'kelvin')),
3395+ (((100, 'kelvin'), (10, 'degR')), (94.44, 'kelvin')),
3396+ (((100, 'kelvin'), (10, 'delta_degC')), (90, 'kelvin')),
3397+ (((100, 'kelvin'), (10, 'delta_degF')), (94.44, 'kelvin')),
3398+
3399+ (((100, 'degC'), (10, 'kelvin')), (363.15, 'delta_degC')),
3400+ (((100, 'degC'), (10, 'degC')), (90, 'delta_degC')),
3401+ (((100, 'degC'), (10, 'degF')), (112.22, 'delta_degC')),
3402+ (((100, 'degC'), (10, 'degR')), (367.59, 'delta_degC')),
3403+ (((100, 'degC'), (10, 'delta_degC')), (90, 'degC')),
3404+ (((100, 'degC'), (10, 'delta_degF')), (94.44, 'degC')),
3405+
3406+ (((100, 'degF'), (10, 'kelvin')), (541.67, 'delta_degF')),
3407+ (((100, 'degF'), (10, 'degC')), (50, 'delta_degF')),
3408+ (((100, 'degF'), (10, 'degF')), (90, 'delta_degF')),
3409+ (((100, 'degF'), (10, 'degR')), (549.67, 'delta_degF')),
3410+ (((100, 'degF'), (10, 'delta_degC')), (82, 'degF')),
3411+ (((100, 'degF'), (10, 'delta_degF')), (90, 'degF')),
3412+
3413+ (((100, 'degR'), (10, 'kelvin')), (82, 'degR')),
3414+ (((100, 'degR'), (10, 'degC')), (-409.67, 'degR')),
3415+ (((100, 'degR'), (10, 'degF')), (-369.67, 'degR')),
3416+ (((100, 'degR'), (10, 'degR')), (90, 'degR')),
3417+ (((100, 'degR'), (10, 'delta_degC')), (82, 'degR')),
3418+ (((100, 'degR'), (10, 'delta_degF')), (90, 'degR')),
3419+
3420+ (((100, 'delta_degC'), (10, 'kelvin')), (90, 'kelvin')),
3421+ (((100, 'delta_degC'), (10, 'degC')), (90, 'degC')),
3422+ (((100, 'delta_degC'), (10, 'degF')), (170, 'degF')),
3423+ (((100, 'delta_degC'), (10, 'degR')), (170, 'degR')),
3424+ (((100, 'delta_degC'), (10, 'delta_degC')), (90, 'delta_degC')),
3425+ (((100, 'delta_degC'), (10, 'delta_degF')), (94.44, 'delta_degC')),
3426+
3427+ (((100, 'delta_degF'), (10, 'kelvin')), (45.56, 'kelvin')),
3428+ (((100, 'delta_degF'), (10, 'degC')), (45.56, 'degC')),
3429+ (((100, 'delta_degF'), (10, 'degF')), (90, 'degF')),
3430+ (((100, 'delta_degF'), (10, 'degR')), (90, 'degR')),
3431+ (((100, 'delta_degF'), (10, 'delta_degC')), (82, 'delta_degF')),
3432+ (((100, 'delta_degF'), (10, 'delta_degF')), (90, 'delta_degF')),
3433+ ]
3434+
3435+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
3436+ subtractions)
3437+ def test_subtraction(self, input_tuple, expected):
3438+ self.ureg.autoconvert_offset_to_baseunit = False
3439+ qin1, qin2 = input_tuple
3440+ q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
3441+ input_tuple = q1, q2
3442+ if expected == 'error':
3443+ self.assertRaises(OffsetUnitCalculusError, op.sub, q1, q2)
3444+ else:
3445+ expected = self.Q_(*expected)
3446+ self.assertEqual(op.sub(q1, q2).units, expected.units)
3447+ self.assertQuantityAlmostEqual(op.sub(q1, q2), expected,
3448+ atol=0.01)
3449+
3450+# @unittest.expectedFailure
3451+ @helpers.requires_numpy()
3452+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
3453+ subtractions)
3454+ def test_inplace_subtraction(self, input_tuple, expected):
3455+ self.ureg.autoconvert_offset_to_baseunit = False
3456+ (q1v, q1u), (q2v, q2u) = input_tuple
3457+ # update input tuple with new values to have correct values on failure
3458+ input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
3459+ (np.array([q2v]*2, dtype=np.float), q2u))
3460+ Q_ = self.Q_
3461+ qin1, qin2 = input_tuple
3462+ q1, q2 = Q_(*qin1), Q_(*qin2)
3463+ q1_cp = copy.copy(q1)
3464+ if expected == 'error':
3465+ self.assertRaises(OffsetUnitCalculusError, op.isub, q1_cp, q2)
3466+ else:
3467+ expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
3468+ self.assertEqual(op.isub(q1_cp, q2).units, Q_(*expected).units)
3469+ q1_cp = copy.copy(q1)
3470+ self.assertQuantityAlmostEqual(op.isub(q1_cp, q2), Q_(*expected),
3471+ atol=0.01)
3472+
3473+ multiplications = [
3474+ (((100, 'kelvin'), (10, 'kelvin')), (1000, 'kelvin**2')),
3475+ (((100, 'kelvin'), (10, 'degC')), 'error'),
3476+ (((100, 'kelvin'), (10, 'degF')), 'error'),
3477+ (((100, 'kelvin'), (10, 'degR')), (1000, 'kelvin*degR')),
3478+ (((100, 'kelvin'), (10, 'delta_degC')), (1000, 'kelvin*delta_degC')),
3479+ (((100, 'kelvin'), (10, 'delta_degF')), (1000, 'kelvin*delta_degF')),
3480+
3481+ (((100, 'degC'), (10, 'kelvin')), 'error'),
3482+ (((100, 'degC'), (10, 'degC')), 'error'),
3483+ (((100, 'degC'), (10, 'degF')), 'error'),
3484+ (((100, 'degC'), (10, 'degR')), 'error'),
3485+ (((100, 'degC'), (10, 'delta_degC')), 'error'),
3486+ (((100, 'degC'), (10, 'delta_degF')), 'error'),
3487+
3488+ (((100, 'degF'), (10, 'kelvin')), 'error'),
3489+ (((100, 'degF'), (10, 'degC')), 'error'),
3490+ (((100, 'degF'), (10, 'degF')), 'error'),
3491+ (((100, 'degF'), (10, 'degR')), 'error'),
3492+ (((100, 'degF'), (10, 'delta_degC')), 'error'),
3493+ (((100, 'degF'), (10, 'delta_degF')), 'error'),
3494+
3495+ (((100, 'degR'), (10, 'kelvin')), (1000, 'degR*kelvin')),
3496+ (((100, 'degR'), (10, 'degC')), 'error'),
3497+ (((100, 'degR'), (10, 'degF')), 'error'),
3498+ (((100, 'degR'), (10, 'degR')), (1000, 'degR**2')),
3499+ (((100, 'degR'), (10, 'delta_degC')), (1000, 'degR*delta_degC')),
3500+ (((100, 'degR'), (10, 'delta_degF')), (1000, 'degR*delta_degF')),
3501+
3502+ (((100, 'delta_degC'), (10, 'kelvin')), (1000, 'delta_degC*kelvin')),
3503+ (((100, 'delta_degC'), (10, 'degC')), 'error'),
3504+ (((100, 'delta_degC'), (10, 'degF')), 'error'),
3505+ (((100, 'delta_degC'), (10, 'degR')), (1000, 'delta_degC*degR')),
3506+ (((100, 'delta_degC'), (10, 'delta_degC')), (1000, 'delta_degC**2')),
3507+ (((100, 'delta_degC'), (10, 'delta_degF')), (1000, 'delta_degC*delta_degF')),
3508+
3509+ (((100, 'delta_degF'), (10, 'kelvin')), (1000, 'delta_degF*kelvin')),
3510+ (((100, 'delta_degF'), (10, 'degC')), 'error'),
3511+ (((100, 'delta_degF'), (10, 'degF')), 'error'),
3512+ (((100, 'delta_degF'), (10, 'degR')), (1000, 'delta_degF*degR')),
3513+ (((100, 'delta_degF'), (10, 'delta_degC')), (1000, 'delta_degF*delta_degC')),
3514+ (((100, 'delta_degF'), (10, 'delta_degF')), (1000, 'delta_degF**2')),
3515+ ]
3516+
3517+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
3518+ multiplications)
3519+ def test_multiplication(self, input_tuple, expected):
3520+ self.ureg.autoconvert_offset_to_baseunit = False
3521+ qin1, qin2 = input_tuple
3522+ q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
3523+ input_tuple = q1, q2
3524+ if expected == 'error':
3525+ self.assertRaises(OffsetUnitCalculusError, op.mul, q1, q2)
3526+ else:
3527+ expected = self.Q_(*expected)
3528+ self.assertEqual(op.mul(q1, q2).units, expected.units)
3529+ self.assertQuantityAlmostEqual(op.mul(q1, q2), expected,
3530+ atol=0.01)
3531+
3532+ @helpers.requires_numpy()
3533+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
3534+ multiplications)
3535+ def test_inplace_multiplication(self, input_tuple, expected):
3536+ self.ureg.autoconvert_offset_to_baseunit = False
3537+ (q1v, q1u), (q2v, q2u) = input_tuple
3538+ # update input tuple with new values to have correct values on failure
3539+ input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
3540+ (np.array([q2v]*2, dtype=np.float), q2u))
3541+ Q_ = self.Q_
3542+ qin1, qin2 = input_tuple
3543+ q1, q2 = Q_(*qin1), Q_(*qin2)
3544+ q1_cp = copy.copy(q1)
3545+ if expected == 'error':
3546+ self.assertRaises(OffsetUnitCalculusError, op.imul, q1_cp, q2)
3547+ else:
3548+ expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
3549+ self.assertEqual(op.imul(q1_cp, q2).units, Q_(*expected).units)
3550+ q1_cp = copy.copy(q1)
3551+ self.assertQuantityAlmostEqual(op.imul(q1_cp, q2), Q_(*expected),
3552+ atol=0.01)
3553+
3554+ divisions = [
3555+ (((100, 'kelvin'), (10, 'kelvin')), (10, '')),
3556+ (((100, 'kelvin'), (10, 'degC')), 'error'),
3557+ (((100, 'kelvin'), (10, 'degF')), 'error'),
3558+ (((100, 'kelvin'), (10, 'degR')), (10, 'kelvin/degR')),
3559+ (((100, 'kelvin'), (10, 'delta_degC')), (10, 'kelvin/delta_degC')),
3560+ (((100, 'kelvin'), (10, 'delta_degF')), (10, 'kelvin/delta_degF')),
3561+
3562+ (((100, 'degC'), (10, 'kelvin')), 'error'),
3563+ (((100, 'degC'), (10, 'degC')), 'error'),
3564+ (((100, 'degC'), (10, 'degF')), 'error'),
3565+ (((100, 'degC'), (10, 'degR')), 'error'),
3566+ (((100, 'degC'), (10, 'delta_degC')), 'error'),
3567+ (((100, 'degC'), (10, 'delta_degF')), 'error'),
3568+
3569+ (((100, 'degF'), (10, 'kelvin')), 'error'),
3570+ (((100, 'degF'), (10, 'degC')), 'error'),
3571+ (((100, 'degF'), (10, 'degF')), 'error'),
3572+ (((100, 'degF'), (10, 'degR')), 'error'),
3573+ (((100, 'degF'), (10, 'delta_degC')), 'error'),
3574+ (((100, 'degF'), (10, 'delta_degF')), 'error'),
3575+
3576+ (((100, 'degR'), (10, 'kelvin')), (10, 'degR/kelvin')),
3577+ (((100, 'degR'), (10, 'degC')), 'error'),
3578+ (((100, 'degR'), (10, 'degF')), 'error'),
3579+ (((100, 'degR'), (10, 'degR')), (10, '')),
3580+ (((100, 'degR'), (10, 'delta_degC')), (10, 'degR/delta_degC')),
3581+ (((100, 'degR'), (10, 'delta_degF')), (10, 'degR/delta_degF')),
3582+
3583+ (((100, 'delta_degC'), (10, 'kelvin')), (10, 'delta_degC/kelvin')),
3584+ (((100, 'delta_degC'), (10, 'degC')), 'error'),
3585+ (((100, 'delta_degC'), (10, 'degF')), 'error'),
3586+ (((100, 'delta_degC'), (10, 'degR')), (10, 'delta_degC/degR')),
3587+ (((100, 'delta_degC'), (10, 'delta_degC')), (10, '')),
3588+ (((100, 'delta_degC'), (10, 'delta_degF')), (10, 'delta_degC/delta_degF')),
3589+
3590+ (((100, 'delta_degF'), (10, 'kelvin')), (10, 'delta_degF/kelvin')),
3591+ (((100, 'delta_degF'), (10, 'degC')), 'error'),
3592+ (((100, 'delta_degF'), (10, 'degF')), 'error'),
3593+ (((100, 'delta_degF'), (10, 'degR')), (10, 'delta_degF/degR')),
3594+ (((100, 'delta_degF'), (10, 'delta_degC')), (10, 'delta_degF/delta_degC')),
3595+ (((100, 'delta_degF'), (10, 'delta_degF')), (10, '')),
3596+ ]
3597+
3598+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
3599+ divisions)
3600+ def test_truedivision(self, input_tuple, expected):
3601+ self.ureg.autoconvert_offset_to_baseunit = False
3602+ qin1, qin2 = input_tuple
3603+ q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
3604+ input_tuple = q1, q2
3605+ if expected == 'error':
3606+ self.assertRaises(OffsetUnitCalculusError, op.truediv, q1, q2)
3607+ else:
3608+ expected = self.Q_(*expected)
3609+ self.assertEqual(op.truediv(q1, q2).units, expected.units)
3610+ self.assertQuantityAlmostEqual(op.truediv(q1, q2), expected,
3611+ atol=0.01)
3612+
3613+ @helpers.requires_numpy()
3614+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
3615+ divisions)
3616+ def test_inplace_truedivision(self, input_tuple, expected):
3617+ self.ureg.autoconvert_offset_to_baseunit = False
3618+ (q1v, q1u), (q2v, q2u) = input_tuple
3619+ # update input tuple with new values to have correct values on failure
3620+ input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
3621+ (np.array([q2v]*2, dtype=np.float), q2u))
3622+ Q_ = self.Q_
3623+ qin1, qin2 = input_tuple
3624+ q1, q2 = Q_(*qin1), Q_(*qin2)
3625+ q1_cp = copy.copy(q1)
3626+ if expected == 'error':
3627+ self.assertRaises(OffsetUnitCalculusError, op.itruediv, q1_cp, q2)
3628+ else:
3629+ expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
3630+ self.assertEqual(op.itruediv(q1_cp, q2).units, Q_(*expected).units)
3631+ q1_cp = copy.copy(q1)
3632+ self.assertQuantityAlmostEqual(op.itruediv(q1_cp, q2),
3633+ Q_(*expected), atol=0.01)
3634+
3635+ multiplications_with_autoconvert_to_baseunit = [
3636+ (((100, 'kelvin'), (10, 'degC')), (28315., 'kelvin**2')),
3637+ (((100, 'kelvin'), (10, 'degF')), (26092.78, 'kelvin**2')),
3638+
3639+ (((100, 'degC'), (10, 'kelvin')), (3731.5, 'kelvin**2')),
3640+ (((100, 'degC'), (10, 'degC')), (105657.42, 'kelvin**2')),
3641+ (((100, 'degC'), (10, 'degF')), (97365.20, 'kelvin**2')),
3642+ (((100, 'degC'), (10, 'degR')), (3731.5, 'kelvin*degR')),
3643+ (((100, 'degC'), (10, 'delta_degC')), (3731.5, 'kelvin*delta_degC')),
3644+ (((100, 'degC'), (10, 'delta_degF')), (3731.5, 'kelvin*delta_degF')),
3645+
3646+ (((100, 'degF'), (10, 'kelvin')), (3109.28, 'kelvin**2')),
3647+ (((100, 'degF'), (10, 'degC')), (88039.20, 'kelvin**2')),
3648+ (((100, 'degF'), (10, 'degF')), (81129.69, 'kelvin**2')),
3649+ (((100, 'degF'), (10, 'degR')), (3109.28, 'kelvin*degR')),
3650+ (((100, 'degF'), (10, 'delta_degC')), (3109.28, 'kelvin*delta_degC')),
3651+ (((100, 'degF'), (10, 'delta_degF')), (3109.28, 'kelvin*delta_degF')),
3652+
3653+ (((100, 'degR'), (10, 'degC')), (28315., 'degR*kelvin')),
3654+ (((100, 'degR'), (10, 'degF')), (26092.78, 'degR*kelvin')),
3655+
3656+ (((100, 'delta_degC'), (10, 'degC')), (28315., 'delta_degC*kelvin')),
3657+ (((100, 'delta_degC'), (10, 'degF')), (26092.78, 'delta_degC*kelvin')),
3658+
3659+ (((100, 'delta_degF'), (10, 'degC')), (28315., 'delta_degF*kelvin')),
3660+ (((100, 'delta_degF'), (10, 'degF')), (26092.78, 'delta_degF*kelvin')),
3661+ ]
3662+
3663+ @ParameterizedTestCase.parameterize(
3664+ ("input", "expected_output"),
3665+ multiplications_with_autoconvert_to_baseunit)
3666+ def test_multiplication_with_autoconvert(self, input_tuple, expected):
3667+ self.ureg.autoconvert_offset_to_baseunit = True
3668+ qin1, qin2 = input_tuple
3669+ q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
3670+ input_tuple = q1, q2
3671+ if expected == 'error':
3672+ self.assertRaises(OffsetUnitCalculusError, op.mul, q1, q2)
3673+ else:
3674+ expected = self.Q_(*expected)
3675+ self.assertEqual(op.mul(q1, q2).units, expected.units)
3676+ self.assertQuantityAlmostEqual(op.mul(q1, q2), expected,
3677+ atol=0.01)
3678+
3679+ @helpers.requires_numpy()
3680+ @ParameterizedTestCase.parameterize(
3681+ ("input", "expected_output"),
3682+ multiplications_with_autoconvert_to_baseunit)
3683+ def test_inplace_multiplication_with_autoconvert(self, input_tuple, expected):
3684+ self.ureg.autoconvert_offset_to_baseunit = True
3685+ (q1v, q1u), (q2v, q2u) = input_tuple
3686+ # update input tuple with new values to have correct values on failure
3687+ input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
3688+ (np.array([q2v]*2, dtype=np.float), q2u))
3689+ Q_ = self.Q_
3690+ qin1, qin2 = input_tuple
3691+ q1, q2 = Q_(*qin1), Q_(*qin2)
3692+ q1_cp = copy.copy(q1)
3693+ if expected == 'error':
3694+ self.assertRaises(OffsetUnitCalculusError, op.imul, q1_cp, q2)
3695+ else:
3696+ expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
3697+ self.assertEqual(op.imul(q1_cp, q2).units, Q_(*expected).units)
3698+ q1_cp = copy.copy(q1)
3699+ self.assertQuantityAlmostEqual(op.imul(q1_cp, q2), Q_(*expected),
3700+ atol=0.01)
3701+
3702+ multiplications_with_scalar = [
3703+ (((10, 'kelvin'), 2), (20., 'kelvin')),
3704+ (((10, 'kelvin**2'), 2), (20., 'kelvin**2')),
3705+ (((10, 'degC'), 2), (20., 'degC')),
3706+ (((10, '1/degC'), 2), 'error'),
3707+ (((10, 'degC**0.5'), 2), 'error'),
3708+ (((10, 'degC**2'), 2), 'error'),
3709+ (((10, 'degC**-2'), 2), 'error'),
3710+ ]
3711+
3712+ @ParameterizedTestCase.parameterize(
3713+ ("input", "expected_output"), multiplications_with_scalar)
3714+ def test_multiplication_with_scalar(self, input_tuple, expected):
3715+ self.ureg.default_as_delta = False
3716+ in1, in2 = input_tuple
3717+ if type(in1) is tuple:
3718+ in1, in2 = self.Q_(*in1), in2
3719+ else:
3720+ in1, in2 = in1, self.Q_(*in2)
3721+ input_tuple = in1, in2 # update input_tuple for better tracebacks
3722+ if expected == 'error':
3723+ self.assertRaises(OffsetUnitCalculusError, op.mul, in1, in2)
3724+ else:
3725+ expected = self.Q_(*expected)
3726+ self.assertEqual(op.mul(in1, in2).units, expected.units)
3727+ self.assertQuantityAlmostEqual(op.mul(in1, in2), expected,
3728+ atol=0.01)
3729+
3730+ divisions_with_scalar = [ # without / with autoconvert to base unit
3731+ (((10, 'kelvin'), 2), [(5., 'kelvin'), (5., 'kelvin')]),
3732+ (((10, 'kelvin**2'), 2), [(5., 'kelvin**2'), (5., 'kelvin**2')]),
3733+ (((10, 'degC'), 2), ['error', 'error']),
3734+ (((10, 'degC**2'), 2), ['error', 'error']),
3735+ (((10, 'degC**-2'), 2), ['error', 'error']),
3736+
3737+ ((2, (10, 'kelvin')), [(0.2, '1/kelvin'), (0.2, '1/kelvin')]),
3738+ ((2, (10, 'degC')), ['error', (2/283.15, '1/kelvin')]),
3739+ ((2, (10, 'degC**2')), ['error', 'error']),
3740+ ((2, (10, 'degC**-2')), ['error', 'error']),
3741+ ]
3742+
3743+ @ParameterizedTestCase.parameterize(
3744+ ("input", "expected_output"), divisions_with_scalar)
3745+ def test_division_with_scalar(self, input_tuple, expected):
3746+ self.ureg.default_as_delta = False
3747+ in1, in2 = input_tuple
3748+ if type(in1) is tuple:
3749+ in1, in2 = self.Q_(*in1), in2
3750+ else:
3751+ in1, in2 = in1, self.Q_(*in2)
3752+ input_tuple = in1, in2 # update input_tuple for better tracebacks
3753+ expected_copy = expected[:]
3754+ for i, mode in enumerate([False, True]):
3755+ self.ureg.autoconvert_offset_to_baseunit = mode
3756+ if expected_copy[i] == 'error':
3757+ self.assertRaises(OffsetUnitCalculusError, op.truediv, in1, in2)
3758+ else:
3759+ expected = self.Q_(*expected_copy[i])
3760+ self.assertEqual(op.truediv(in1, in2).units, expected.units)
3761+ self.assertQuantityAlmostEqual(op.truediv(in1, in2), expected)
3762+
3763+ exponentiation = [ # resuls without / with autoconvert
3764+ (((10, 'degC'), 1), [(10, 'degC'), (10, 'degC')]),
3765+ (((10, 'degC'), 0.5), ['error', (283.15**0.5, 'kelvin**0.5')]),
3766+ (((10, 'degC'), 0), [(1., ''), (1., '')]),
3767+ (((10, 'degC'), -1), ['error', (1/(10+273.15), 'kelvin**-1')]),
3768+ (((10, 'degC'), -2), ['error', (1/(10+273.15)**2., 'kelvin**-2')]),
3769+ ((( 0, 'degC'), -2), ['error', (1/(273.15)**2, 'kelvin**-2')]),
3770+ (((10, 'degC'), (2, '')), ['error', ((283.15)**2, 'kelvin**2')]),
3771+ (((10, 'degC'), (10, 'degK')), ['error', 'error']),
3772+
3773+ (((10, 'kelvin'), (2, '')), [(100., 'kelvin**2'), (100., 'kelvin**2')]),
3774+
3775+ (( 2, (2, 'kelvin')), ['error', 'error']),
3776+ (( 2, (500., 'millikelvin/kelvin')), [2**0.5, 2**0.5]),
3777+ (( 2, (0.5, 'kelvin/kelvin')), [2**0.5, 2**0.5]),
3778+ (((10, 'degC'), (500., 'millikelvin/kelvin')),
3779+ ['error', (283.15**0.5, 'kelvin**0.5')]),
3780+ ]
3781+
3782+ @ParameterizedTestCase.parameterize(
3783+ ("input", "expected_output"), exponentiation)
3784+ def test_exponentiation(self, input_tuple, expected):
3785+ self.ureg.default_as_delta = False
3786+ in1, in2 = input_tuple
3787+ if type(in1) is tuple and type(in2) is tuple:
3788+ in1, in2 = self.Q_(*in1), self.Q_(*in2)
3789+ elif not type(in1) is tuple and type(in2) is tuple:
3790+ in2 = self.Q_(*in2)
3791+ else:
3792+ in1 = self.Q_(*in1)
3793+ input_tuple = in1, in2
3794+ expected_copy = expected[:]
3795+ for i, mode in enumerate([False, True]):
3796+ self.ureg.autoconvert_offset_to_baseunit = mode
3797+ if expected_copy[i] == 'error':
3798+ self.assertRaises((OffsetUnitCalculusError,
3799+ DimensionalityError), op.pow, in1, in2)
3800+ else:
3801+ if type(expected_copy[i]) is tuple:
3802+ expected = self.Q_(*expected_copy[i])
3803+ self.assertEqual(op.pow(in1, in2).units, expected.units)
3804+ else:
3805+ expected = expected_copy[i]
3806+ self.assertQuantityAlmostEqual(op.pow(in1, in2), expected)
3807+
3808+ @helpers.requires_numpy()
3809+ @ParameterizedTestCase.parameterize(
3810+ ("input", "expected_output"), exponentiation)
3811+ def test_inplace_exponentiation(self, input_tuple, expected):
3812+ self.ureg.default_as_delta = False
3813+ in1, in2 = input_tuple
3814+ if type(in1) is tuple and type(in2) is tuple:
3815+ (q1v, q1u), (q2v, q2u) = in1, in2
3816+ in1 = self.Q_(*(np.array([q1v]*2, dtype=np.float), q1u))
3817+ in2 = self.Q_(q2v, q2u)
3818+ elif not type(in1) is tuple and type(in2) is tuple:
3819+ in2 = self.Q_(*in2)
3820+ else:
3821+ in1 = self.Q_(*in1)
3822+
3823+ input_tuple = in1, in2
3824+
3825+ expected_copy = expected[:]
3826+ for i, mode in enumerate([False, True]):
3827+ self.ureg.autoconvert_offset_to_baseunit = mode
3828+ in1_cp = copy.copy(in1)
3829+ if expected_copy[i] == 'error':
3830+ self.assertRaises((OffsetUnitCalculusError,
3831+ DimensionalityError), op.ipow, in1_cp, in2)
3832+ else:
3833+ if type(expected_copy[i]) is tuple:
3834+ expected = self.Q_(np.array([expected_copy[i][0]]*2,
3835+ dtype=np.float),
3836+ expected_copy[i][1])
3837+ self.assertEqual(op.ipow(in1_cp, in2).units, expected.units)
3838+ else:
3839+ expected = np.array([expected_copy[i]]*2, dtype=np.float)
3840+
3841+
3842+ in1_cp = copy.copy(in1)
3843+ self.assertQuantityAlmostEqual(op.ipow(in1_cp, in2), expected)
3844
3845=== modified file 'pint/testsuite/test_umath.py'
3846--- pint/testsuite/test_umath.py 2014-09-05 23:26:05 +0000
3847+++ pint/testsuite/test_umath.py 2015-01-07 15:51:38 +0000
3848@@ -52,17 +52,29 @@
3849 except Exception as e:
3850 self.assertFalse(True, msg='{0} not raised but {1}\n{2}'.format(ExcType, e, msg))
3851
3852- def _testn(self, func, ok_with, raise_with=(), results=None):
3853- self._test1(func, ok_with, raise_with, output_units=None, results=results)
3854-
3855 def _test1(self, func, ok_with, raise_with=(), output_units='same', results=None, rtol=1e-6):
3856+ """Test function that takes a single argument and returns Quantity.
3857+
3858+ :param func: function callable.
3859+ :param ok_with: iterables of values that work fine.
3860+ :param raise_with: iterables of values that raise exceptions.
3861+ :param output_units: units to be used when building results.
3862+ 'same': ok_with[n].units (default).
3863+ is float: ok_with[n].units ** output_units.
3864+ None: no output units, the result should be an ndarray.
3865+ Other value will be parsed as unit.
3866+ :param results: iterable of results.
3867+ If None, the result will be obtained by applying
3868+ func to each ok_with value
3869+ :param rtol: relative tolerance.
3870+ """
3871 if results is None:
3872 results = [None, ] * len(ok_with)
3873 for x1, res in zip(ok_with, results):
3874 err_msg = 'At {0} with {1}'.format(func.__name__, x1)
3875 if output_units == 'same':
3876 ou = x1.units
3877- elif isinstance(output_units, int):
3878+ elif isinstance(output_units, (int, float)):
3879 ou = x1.units ** output_units
3880 else:
3881 ou = output_units
3882@@ -70,20 +82,45 @@
3883 qm = func(x1)
3884 if res is None:
3885 res = func(x1.magnitude)
3886- if ou:
3887+ if ou is not None:
3888 res = self.Q_(res, ou)
3889- if isinstance(res, self.Q_):
3890- self.assertIsInstance(qm, self.Q_, msg=err_msg + ' {0!r} is not Quantity'.format(qm))
3891- qm = qm.magnitude
3892- res = res.magnitude
3893- np.testing.assert_allclose(qm, res, rtol=rtol, err_msg=err_msg)
3894+
3895+ self.assertQuantityAlmostEqual(qm, res, rtol=rtol, msg=err_msg)
3896
3897 for x1 in raise_with:
3898 self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1),
3899 ValueError, func, x1)
3900
3901+ def _testn(self, func, ok_with, raise_with=(), results=None):
3902+ """Test function that takes a single argument and returns and ndarray (not a Quantity)
3903+
3904+ :param func: function callable.
3905+ :param ok_with: iterables of values that work fine.
3906+ :param raise_with: iterables of values that raise exceptions.
3907+ :param results: iterable of results.
3908+ If None, the result will be obtained by applying
3909+ func to each ok_with value
3910+ """
3911+ self._test1(func, ok_with, raise_with, output_units=None, results=results)
3912+
3913 def _test1_2o(self, func, ok_with, raise_with=(), output_units=('same', 'same'),
3914 results=None, rtol=1e-6):
3915+ """Test functions that takes a single argument and return two Quantities.
3916+
3917+ :param func: function callable.
3918+ :param ok_with: iterables of values that work fine.
3919+ :param raise_with: iterables of values that raise exceptions.
3920+ :param output_units: tuple of units to be used when building the result tuple.
3921+ 'same': ok_with[n].units (default).
3922+ is float: ok_with[n].units ** output_units.
3923+ None: no output units, the result should be an ndarray.
3924+ Other value will be parsed as unit.
3925+ :param results: iterable of results.
3926+ If None, the result will be obtained by applying
3927+ func to each ok_with value
3928+ :param rtol: relative tolerance.
3929+ """
3930+
3931 if results is None:
3932 results = [None, ] * len(ok_with)
3933 for x1, res in zip(ok_with, results):
3934@@ -95,26 +132,35 @@
3935 for ndx, (qm, re, ou) in enumerate(zip(qms, res, output_units)):
3936 if ou == 'same':
3937 ou = x1.units
3938- elif isinstance(ou, int):
3939+ elif isinstance(ou, (int, float)):
3940 ou = x1.units ** ou
3941
3942 if ou is not None:
3943 re = self.Q_(re, ou)
3944
3945- if isinstance(re, self.Q_):
3946- self.assertIsInstance(qm, self.Q_, msg=err_msg)
3947- qm = qm.magnitude
3948- re = re.magnitude
3949- np.testing.assert_allclose(qm, re, rtol=rtol, err_msg=err_msg)
3950+ self.assertQuantityAlmostEqual(qm, re, rtol=rtol, msg=err_msg)
3951
3952 for x1 in raise_with:
3953 self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1),
3954 ValueError, func, x1)
3955
3956- def _testn2(self, func, x1, ok_with, raise_with=()):
3957- self._test2(func, x1, ok_with, raise_with, output_units=None)
3958-
3959 def _test2(self, func, x1, ok_with, raise_with=(), output_units='same', rtol=1e-6, convert2=True):
3960+ """Test function that takes two arguments and return a Quantity.
3961+
3962+ :param func: function callable.
3963+ :param x1: first argument of func.
3964+ :param ok_with: iterables of values that work fine.
3965+ :param raise_with: iterables of values that raise exceptions.
3966+ :param output_units: units to be used when building results.
3967+ 'same': x1.units (default).
3968+ 'prod': x1.units * ok_with[n].units
3969+ 'div': x1.units / ok_with[n].units
3970+ 'second': x1.units * ok_with[n]
3971+ None: no output units, the result should be an ndarray.
3972+ Other value will be parsed as unit.
3973+ :param rtol: relative tolerance.
3974+ :param convert2: if the ok_with[n] should be converted to x1.units.
3975+ """
3976 for x2 in ok_with:
3977 err_msg = 'At {0} with {1} and {2}'.format(func.__name__, x1, x2)
3978 if output_units == 'same':
3979@@ -136,18 +182,25 @@
3980 m2 = getattr(x2, 'magnitude', x2)
3981
3982 res = func(x1.magnitude, m2)
3983- if ou:
3984+ if ou is not None:
3985 res = self.Q_(res, ou)
3986- if isinstance(res, self.Q_):
3987- self.assertIsInstance(qm, self.Q_, msg=err_msg)
3988- qm = qm.magnitude
3989- res = res.magnitude
3990- np.testing.assert_allclose(qm, res, rtol=rtol, err_msg=err_msg)
3991+
3992+ self.assertQuantityAlmostEqual(qm, res, rtol=rtol, msg=err_msg)
3993
3994 for x2 in raise_with:
3995 self.assertRaisesMsg('At {0} with {1} and {2}'.format(func.__name__, x1, x2),
3996 ValueError, func, x1, x2)
3997
3998+ def _testn2(self, func, x1, ok_with, raise_with=()):
3999+ """Test function that takes two arguments and return a ndarray.
4000+
4001+ :param func: function callable.
4002+ :param x1: first argument of func.
4003+ :param ok_with: iterables of values that work fine.
4004+ :param raise_with: iterables of values that raise exceptions.
4005+ """
4006+ self._test2(func, x1, ok_with, raise_with, output_units=None)
4007+
4008
4009 @helpers.requires_numpy()
4010 class TestMathUfuncs(TestUFuncs):
4011@@ -248,7 +301,7 @@
4012 self.q1,
4013 (self.q2, self.qs, self.qless),
4014 (),
4015- 'div', convert2=False)
4016+ 'same', convert2=False)
4017
4018 def test_mod(self):
4019 self._test2(np.mod,
4020@@ -322,7 +375,7 @@
4021 self._test1(np.sqrt,
4022 (self.q2, self.qs, self.qless, self.qi),
4023 (),
4024- 'same')
4025+ 0.5)
4026
4027 def test_square(self):
4028 self._test1(np.square,
4029@@ -334,7 +387,7 @@
4030 self._test1(np.reciprocal,
4031 (self.q2, self.qs, self.qless, self.qi),
4032 (),
4033- 2)
4034+ -1)
4035
4036
4037 @helpers.requires_numpy()
4038@@ -469,10 +522,16 @@
4039 ), (self.ureg.m, ), 'radians')
4040
4041 def test_rad2deg(self):
4042- self._test1(np.rad2deg, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless,
4043- np.arange(0, pi/2, pi/4) * self.ureg.radian,
4044- np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m
4045- ), (self.ureg.m, ), 'degree', results=(None, None, np.rad2deg(np.arange(0, pi/2, pi/4)*0.001)))
4046+ self._test1(np.rad2deg,
4047+ (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless,
4048+ np.arange(0, pi/2, pi/4) * self.ureg.radian,
4049+ np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m,
4050+ ),
4051+ (self.ureg.m, ), 'degree',
4052+ results=(None,
4053+ None,
4054+ np.rad2deg(np.arange(0, pi/2, pi/4)*0.001) * self.ureg.degree,
4055+ ))
4056
4057
4058
4059
4060=== modified file 'pint/testsuite/test_unit.py'
4061--- pint/testsuite/test_unit.py 2014-09-05 23:26:05 +0000
4062+++ pint/testsuite/test_unit.py 2015-01-07 15:51:38 +0000
4063@@ -12,8 +12,9 @@
4064 DimensionDefinition, _freeze, Converter, UnitRegistry,
4065 LazyRegistry, ParserHelper)
4066 from pint import DimensionalityError, UndefinedUnitError
4067-from pint.compat import u, unittest, np
4068+from pint.compat import u, unittest, np, string_types
4069 from pint.testsuite import QuantityTestCase, helpers, BaseTestCase
4070+from pint.testsuite.parameterized import ParameterizedTestCase
4071
4072
4073 class TestConverter(BaseTestCase):
4074@@ -231,6 +232,9 @@
4075
4076 FORCE_NDARRAY = False
4077
4078+ def setup(self):
4079+ self.ureg.autoconvert_offset_to_baseunit = False
4080+
4081 def test_base(self):
4082 ureg = UnitRegistry(None)
4083 ureg.define('meter = [length]')
4084@@ -324,16 +328,16 @@
4085 q = self.Q_(1, 'g/(m**2*s)')
4086 self.assertEqual(self.Q_(q.magnitude, str(q.units)), q)
4087
4088- def test_to_delta(self):
4089+ def test_as_delta(self):
4090 parse = self.ureg.parse_units
4091- self.assertEqual(parse('kelvin', to_delta=True), UnitsContainer(kelvin=1))
4092- self.assertEqual(parse('kelvin', to_delta=False), UnitsContainer(kelvin=1))
4093- self.assertEqual(parse('kelvin**(-1)', to_delta=True), UnitsContainer(kelvin=-1))
4094- self.assertEqual(parse('kelvin**(-1)', to_delta=False), UnitsContainer(kelvin=-1))
4095- self.assertEqual(parse('kelvin**2', to_delta=True), UnitsContainer(kelvin=2))
4096- self.assertEqual(parse('kelvin**2', to_delta=False), UnitsContainer(kelvin=2))
4097- self.assertEqual(parse('kelvin*meter', to_delta=True), UnitsContainer(kelvin=1, meter= 1))
4098- self.assertEqual(parse('kelvin*meter', to_delta=False), UnitsContainer(kelvin=1, meter=1))
4099+ self.assertEqual(parse('kelvin', as_delta=True), UnitsContainer(kelvin=1))
4100+ self.assertEqual(parse('kelvin', as_delta=False), UnitsContainer(kelvin=1))
4101+ self.assertEqual(parse('kelvin**(-1)', as_delta=True), UnitsContainer(kelvin=-1))
4102+ self.assertEqual(parse('kelvin**(-1)', as_delta=False), UnitsContainer(kelvin=-1))
4103+ self.assertEqual(parse('kelvin**2', as_delta=True), UnitsContainer(kelvin=2))
4104+ self.assertEqual(parse('kelvin**2', as_delta=False), UnitsContainer(kelvin=2))
4105+ self.assertEqual(parse('kelvin*meter', as_delta=True), UnitsContainer(kelvin=1, meter= 1))
4106+ self.assertEqual(parse('kelvin*meter', as_delta=False), UnitsContainer(kelvin=1, meter=1))
4107
4108 def test_name(self):
4109 self.assertRaises(UndefinedUnitError, self.ureg.get_name, 'asdf')
4110@@ -357,18 +361,6 @@
4111 self.assertEqual(self.ureg.get_symbol('international_foot'), 'ft')
4112 self.assertEqual(self.ureg.get_symbol('international_inch'), 'in')
4113
4114- @unittest.expectedFailure
4115- def test_delta_in_diff(self):
4116- """This might be supported in future versions
4117- """
4118- xk = 1 * self.ureg.kelvin
4119- yk = 2 * self.ureg.kelvin
4120- yf = yk.to('degF')
4121- yc = yk.to('degC')
4122- self.assertEqual(yk - xk, 1 * self.ureg.kelvin)
4123- self.assertEqual(yf - xk, 1 * self.ureg.kelvin)
4124- self.assertEqual(yc - xk, 1 * self.ureg.kelvin)
4125-
4126 def test_pint(self):
4127 p = self.ureg.pint
4128 l = self.ureg.liter
4129@@ -440,12 +432,13 @@
4130 self.assertEqual(h2(3, 1), (3 * ureg.meter, 1 * ureg.cm))
4131
4132 def test_to_ref_vs_to(self):
4133+ self.ureg.autoconvert_offset_to_baseunit = True
4134 q = 8. * self.ureg.inch
4135 t = 8. * self.ureg.degF
4136 dt = 8. * self.ureg.delta_degF
4137 self.assertEqual(q.to('cm').magnitude, self.ureg._units['inch'].converter.to_reference(8.))
4138 self.assertEqual(t.to('kelvin').magnitude, self.ureg._units['degF'].converter.to_reference(8.))
4139- self.assertEqual(dt.to('delta_kelvin').magnitude, self.ureg._units['delta_degF'].converter.to_reference(8.))
4140+ self.assertEqual(dt.to('kelvin').magnitude, self.ureg._units['delta_degF'].converter.to_reference(8.))
4141
4142 def test_redefinition(self):
4143 d = UnitRegistry().define
4144@@ -473,7 +466,7 @@
4145 # Conversions with single units take a different codepath than
4146 # Conversions with more than one unit.
4147 src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1)
4148- src_dst2 = UnitsContainer(meter=1, seconds=-1), UnitsContainer(inch=1, minutes=-1)
4149+ src_dst2 = UnitsContainer(meter=1, second=-1), UnitsContainer(inch=1, minute=-1)
4150 for src, dst in (src_dst1, src_dst2):
4151 v = ureg.convert(1, src, dst),
4152
4153@@ -598,3 +591,52 @@
4154 msg = "Cannot convert from 'a' (c) to 'b' (d)msg"
4155 ex = DimensionalityError('a', 'b', 'c', 'd', 'msg')
4156 self.assertEqual(str(ex), msg)
4157+
4158+
4159+class TestConvertWithOffset(QuantityTestCase, ParameterizedTestCase):
4160+
4161+ # The dicts in convert_with_offset are used to create a UnitsContainer.
4162+ # We create UnitsContainer to avoid any auto-conversion of units.
4163+ convert_with_offset = [
4164+ (({'degC': 1}, {'degC': 1}), 10),
4165+ (({'degC': 1}, {'kelvin': 1}), 283.15),
4166+ (({'degC': 1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'),
4167+ (({'degC': 1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 283150),
4168+
4169+ (({'kelvin': 1}, {'degC': 1}), -263.15),
4170+ (({'kelvin': 1}, {'kelvin': 1}), 10),
4171+ (({'kelvin': 1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'),
4172+ (({'kelvin': 1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 10000),
4173+
4174+ (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1}), 'error'),
4175+ (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1}), 'error'),
4176+ (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 10),
4177+ (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 'error'),
4178+
4179+ (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1}), -273.14),
4180+ (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1}), 0.01),
4181+ (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'),
4182+ (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 10),
4183+
4184+ (({'degC': 2}, {'kelvin': 2}), 'error'),
4185+ (({'degC': 1, 'degF': 1}, {'kelvin': 2}), 'error'),
4186+ (({'degC': 1, 'kelvin': 1}, {'kelvin': 2}), 'error'),
4187+ ]
4188+
4189+ @ParameterizedTestCase.parameterize(("input", "expected_output"),
4190+ convert_with_offset)
4191+ def test_to_and_from_offset_units(self, input_tuple, expected):
4192+ src, dst = input_tuple
4193+ src, dst = UnitsContainer(src), UnitsContainer(dst)
4194+ value = 10.
4195+ convert = self.ureg.convert
4196+ if isinstance(expected, string_types):
4197+ self.assertRaises(DimensionalityError, convert, value, src, dst)
4198+ if src != dst:
4199+ self.assertRaises(DimensionalityError, convert, value, dst, src)
4200+ else:
4201+ self.assertQuantityAlmostEqual(convert(value, src, dst),
4202+ expected, atol=0.001)
4203+ if src != dst:
4204+ self.assertQuantityAlmostEqual(convert(expected, dst, src),
4205+ value, atol=0.001)
4206
4207=== modified file 'pint/unit.py'
4208--- pint/unit.py 2014-09-05 23:26:05 +0000
4209+++ pint/unit.py 2015-01-07 15:51:38 +0000
4210@@ -87,7 +87,7 @@
4211 """Raised when trying to convert between incompatible units.
4212 """
4213
4214- def __init__(self, units1, units2, dim1=None, dim2=None, extra_msg=None):
4215+ def __init__(self, units1, units2, dim1=None, dim2=None, extra_msg=''):
4216 super(DimensionalityError, self).__init__()
4217 self.units1 = units1
4218 self.units2 = units2
4219@@ -103,12 +103,25 @@
4220 dim1 = ''
4221 dim2 = ''
4222
4223- msg = "Cannot convert from '{0}'{1} to '{2}'{3}".format(self.units1, dim1, self.units2, dim2)
4224-
4225- if self.extra_msg:
4226- return msg + self.extra_msg
4227-
4228- return msg
4229+ msg = "Cannot convert from '{0}'{1} to '{2}'{3}" + self.extra_msg
4230+
4231+ return msg.format(self.units1, dim1, self.units2, dim2)
4232+
4233+
4234+class OffsetUnitCalculusError(ValueError):
4235+ """Raised on ambiguous operations with offset units.
4236+ """
4237+ def __init__(self, units1, units2='', extra_msg=''):
4238+ super(ValueError, self).__init__()
4239+ self.units1 = units1
4240+ self.units2 = units2
4241+ self.extra_msg = extra_msg
4242+
4243+ def __str__(self):
4244+ msg = ("Ambiguous operation with offset unit (%s)." %
4245+ ', '.join(['%s' % u for u in [self.units1, self.units2] if u])
4246+ + self.extra_msg)
4247+ return msg.format(self.units1, self.units2)
4248
4249
4250 class Converter(object):
4251@@ -244,7 +257,7 @@
4252
4253
4254 def _is_dim(name):
4255- return name.startswith('[') and name.endswith(']')
4256+ return name[0] == '[' and name[-1] == ']'
4257
4258
4259 class PrefixDefinition(Definition):
4260@@ -289,7 +302,7 @@
4261 'Base units must be referenced only to dimensions. '
4262 'Derived units must be referenced only to units.')
4263 self.reference = UnitsContainer(converter.items())
4264- if 'offset' in modifiers:
4265+ if modifiers.get('offset', 0.) != 0.:
4266 converter = OffsetConverter(converter.scale, modifiers['offset'])
4267 else:
4268 converter = ScaleConverter(converter.scale)
4269@@ -445,14 +458,19 @@
4270 Empty to load the default definition file.
4271 None to leave the UnitRegistry empty.
4272 :param force_ndarray: convert any input, scalar or not to a numpy.ndarray.
4273- :param default_to_delta: In the context of a multiplication of units, interpret
4274+ :param default_as_delta: In the context of a multiplication of units, interpret
4275 non-multiplicative units as their *delta* counterparts.
4276+ :autoconvert_offset_to_baseunit: If True converts offset units in quantites are
4277+ converted to their base units in multiplicative
4278+ context. If False no conversion happens.
4279 :param on_redefinition: action to take in case a unit is redefined.
4280 'warn', 'raise', 'ignore'
4281 :type on_redefintion: str
4282 """
4283
4284- def __init__(self, filename='', force_ndarray=False, default_to_delta=True, on_redefinition='warn'):
4285+ def __init__(self, filename='', force_ndarray=False, default_as_delta=True,
4286+ autoconvert_offset_to_baseunit=False,
4287+ on_redefinition='warn'):
4288 self.Quantity = build_quantity_class(self, force_ndarray)
4289 self.Measurement = build_measurement_class(self, force_ndarray)
4290
4291@@ -491,9 +509,16 @@
4292 #: Maps dimensionality (_freeze(UnitsContainer)) to Units (_freeze(UnitsContainer))
4293 self._dimensionality_cache = TransformDict(_freeze)
4294
4295+ #: Cache the unit name associated to user input. ('mV' -> 'millivolt')
4296+ self._parse_unit_cache = dict()
4297+
4298 #: When performing a multiplication of units, interpret
4299 #: non-multiplicative units as their *delta* counterparts.
4300- self.default_to_delta = default_to_delta
4301+ self.default_as_delta = default_as_delta
4302+
4303+ # Determines if quantities with offset units are converted to their
4304+ # base units on multiplication and division.
4305+ self.autoconvert_offset_to_baseunit = autoconvert_offset_to_baseunit
4306
4307 if filename == '':
4308 self.load_definitions('default_en.txt', True)
4309@@ -504,6 +529,9 @@
4310
4311 self._build_cache()
4312
4313+ def __name__(self):
4314+ return 'UnitRegistry'
4315+
4316 def __getattr__(self, item):
4317 return self.Quantity(1, item)
4318
4319@@ -697,7 +725,8 @@
4320
4321 _adder(alias, definition)
4322
4323- if isinstance(definition.converter, OffsetConverter):
4324+ # define additional "delta_" units for units with an offset
4325+ if getattr(definition.converter, "offset", 0.0) != 0.0:
4326 d_name = 'delta_' + definition.name
4327 if definition.symbol:
4328 d_symbol = 'Δ' + definition.symbol
4329@@ -710,7 +739,7 @@
4330 return '[delta_' + _name[1:]
4331 return 'delta_' + _name
4332
4333- d_reference = UnitsContainer(dict((prep(ref), value)
4334+ d_reference = UnitsContainer(dict((ref, value)
4335 for ref, value in definition.reference.items()))
4336
4337 self.define(UnitDefinition(d_name, d_symbol, d_aliases,
4338@@ -818,7 +847,7 @@
4339 return ''
4340
4341 try:
4342- return self._units[name_or_alias].name
4343+ return self._units[name_or_alias]._name
4344 except KeyError:
4345 pass
4346
4347@@ -857,15 +886,17 @@
4348
4349 return self._prefixes[prefix].symbol + self._units[unit_name].symbol
4350
4351+ def _get_symbol(self, name):
4352+ return self._units[name].symbol
4353+
4354 def get_dimensionality(self, input_units):
4355 """Convert unit or dict of units or dimensions to a dict of base dimensions
4356
4357 :param input_units:
4358 :return: dimensionality
4359 """
4360- dims = UnitsContainer()
4361 if not input_units:
4362- return dims
4363+ return UnitsContainer()
4364
4365 if isinstance(input_units, string_types):
4366 input_units = ParserHelper.from_string(input_units)
4367@@ -873,24 +904,31 @@
4368 if input_units in self._dimensionality_cache:
4369 return copy.copy(self._dimensionality_cache[input_units])
4370
4371- for key, value in input_units.items():
4372+ accumulator = defaultdict(float)
4373+ self._get_dimensionality_recurse(input_units, 1.0, accumulator)
4374+
4375+ dims = UnitsContainer(dict((k, v) for k, v in accumulator.items() if v != 0.))
4376+
4377+ if '[]' in dims:
4378+ del dims['[]']
4379+
4380+ self._dimensionality_cache[input_units] = copy.copy(dims)
4381+
4382+ return dims
4383+
4384+ def _get_dimensionality_recurse(self, ref, exp, accumulator):
4385+ for key in ref:
4386+ exp2 = exp*ref[key]
4387 if _is_dim(key):
4388 reg = self._dimensions[key]
4389 if reg.is_base:
4390- dims.add(key, value)
4391- else:
4392- dims *= self.get_dimensionality(reg.reference) ** value
4393+ accumulator[key] += exp2
4394+ elif reg.reference is not None:
4395+ self._get_dimensionality_recurse(reg.reference, exp2, accumulator)
4396 else:
4397 reg = self._units[self.get_name(key)]
4398- if reg.is_base:
4399- dims *= reg.reference ** value
4400- else:
4401- dims *= self.get_dimensionality(reg.reference) ** value
4402-
4403- if '[]' in dims:
4404- del dims['[]']
4405-
4406- return dims
4407+ if reg.reference is not None:
4408+ self._get_dimensionality_recurse(reg.reference, exp2, accumulator)
4409
4410 def get_base_units(self, input_units, check_nonmult=True):
4411 """Convert unit or dict of units to the base units.
4412@@ -914,27 +952,32 @@
4413 if check_nonmult and input_units in self._base_units_cache:
4414 return copy.deepcopy(self._base_units_cache[input_units])
4415
4416- factor = 1.
4417- units = UnitsContainer()
4418- for key, value in input_units.items():
4419- key = self.get_name(key)
4420- reg = self._units[key]
4421- if reg.is_base:
4422- units.add(key, value)
4423- else:
4424- fac, uni = self.get_base_units(reg.reference, check_nonmult=False)
4425- if factor is not None:
4426- factor *= (reg.converter.scale * fac) ** value
4427- units *= uni ** value
4428+ accumulators = [1., defaultdict(float)]
4429+ self._get_base_units(input_units, 1.0, accumulators)
4430+
4431+ factor = accumulators[0]
4432+ units = UnitsContainer(dict((k, v) for k, v in accumulators[1].items() if v != 0.))
4433
4434 # Check if any of the final units is non multiplicative and return None instead.
4435 if check_nonmult:
4436 for unit in units.keys():
4437- if not isinstance(self._units[unit].converter, ScaleConverter):
4438+ if not self._units[unit].converter.is_multiplicative:
4439 return None, units
4440
4441 return factor, units
4442
4443+ def _get_base_units(self, ref, exp, accumulators):
4444+ for key in ref:
4445+ exp2 = exp*ref[key]
4446+ key = self.get_name(key)
4447+ reg = self._units[key]
4448+ if reg.is_base:
4449+ accumulators[1][key] += exp2
4450+ else:
4451+ accumulators[0] *= reg._converter.scale ** exp2
4452+ if reg.reference is not None:
4453+ self._get_base_units(reg.reference, exp2, accumulators)
4454+
4455 def get_compatible_units(self, input_units):
4456 if not input_units:
4457 return 1., UnitsContainer()
4458@@ -967,9 +1010,9 @@
4459 :return: converted value
4460 """
4461 if isinstance(src, string_types):
4462- src = ParserHelper.from_string(src)
4463+ src = self.parse_units(src)
4464 if isinstance(dst, string_types):
4465- dst = ParserHelper.from_string(dst)
4466+ dst = self.parse_units(dst)
4467 if src == dst:
4468 return value
4469
4470@@ -997,36 +1040,59 @@
4471 if src_dim != dst_dim:
4472 raise DimensionalityError(src, dst, src_dim, dst_dim)
4473
4474- if len(src) == 1:
4475- # If the source has a single element, it might be a non-multiplicative unit
4476- # and therefore it is treated differently.
4477- src_unit, src_value = list(src.items())[0]
4478- src_unit = self._units[self.get_name(src_unit)]
4479-
4480- # We only continue if is a ScaleConverter,
4481- # if not just exit to use the standard src / dst.
4482- # TODO: This will fail and should not degK * meter / nanometer -> degC
4483- if not isinstance(src_unit.converter, ScaleConverter):
4484-
4485- if len(dst) > 1:
4486- # If the destination has more than one element,
4487- # then the conversion is not possible.
4488- # TODO: This will fail and should not degC -> degK * meter / nanometer
4489- raise DimensionalityError(src, dst, src_dim, dst_dim)
4490-
4491- dst_unit, dst_value = list(dst.items())[0]
4492- dst_unit = self._units[self.get_name(dst_unit)]
4493- if not type(src_unit.converter) is type(dst_unit.converter):
4494- raise DimensionalityError(src, dst, src_dim, dst_dim)
4495-
4496- return dst_unit.converter.from_reference(src_unit.converter.to_reference(value, inplace), inplace)
4497-
4498+ # Conversion needs to consider if non-multiplicative (AKA offset
4499+ # units) are involved. Conversion is only possible if src and dst
4500+ # have at most one offset unit per dimension.
4501+ src_offset_units = [(u, e) for u, e in src.items()
4502+ if not self._units[u].is_multiplicative]
4503+ dst_offset_units = [(u, e) for u, e in dst.items()
4504+ if not self._units[u].is_multiplicative]
4505+
4506+ # For offset units we need to check if the conversion is allowed.
4507+ if src_offset_units or dst_offset_units:
4508+
4509+ # Validate that not more than one offset unit is present
4510+ if len(src_offset_units) > 1 or len(dst_offset_units) > 1:
4511+ raise DimensionalityError(
4512+ src, dst, src_dim, dst_dim,
4513+ extra_msg=' - more than one offset unit.')
4514+
4515+ # validate that offset unit is not used in multiplicative context
4516+ if ((len(src_offset_units) == 1 and len(src) > 1)
4517+ or (len(dst_offset_units) == 1 and len(dst) > 1)
4518+ and not self.autoconvert_offset_to_baseunit):
4519+ raise DimensionalityError(
4520+ src, dst, src_dim, dst_dim,
4521+ extra_msg=' - offset unit used in multiplicative context.')
4522+
4523+ # Validate that order of offset unit is exactly one.
4524+ if src_offset_units:
4525+ if src_offset_units[0][1] != 1:
4526+ raise DimensionalityError(
4527+ src, dst, src_dim, dst_dim,
4528+ extra_msg=' - offset units in higher order.')
4529+ else:
4530+ if dst_offset_units[0][1] != 1:
4531+ raise DimensionalityError(
4532+ src, dst, src_dim, dst_dim,
4533+ extra_msg=' - offset units in higher order.')
4534+
4535+ # Here we convert only the offset quantities. Any remaining scaled
4536+ # quantities will be converted later.
4537+
4538+ # clean src from offset units by converting to reference
4539+ for u, e in src_offset_units:
4540+ value = self._units[u].converter.to_reference(value, inplace)
4541+ src.pop(u)
4542+
4543+ # clean dst units from offset units
4544+ for u, e in dst_offset_units:
4545+ dst.pop(u)
4546+
4547+ # Here src and dst have only multiplicative units left. Thus we can
4548+ # convert with a factor.
4549 factor, units = self.get_base_units(src / dst)
4550
4551- if factor is None:
4552- raise DimensionalityError(src, dst, src_dim, dst_dim,
4553- 'Non-multiplicative unit found.')
4554-
4555 # factor is type float and if our magnitude is type Decimal then
4556 # must first convert to Decimal before we can '*' the values
4557 if isinstance(value, Decimal):
4558@@ -1037,6 +1103,16 @@
4559 else:
4560 value = value * factor
4561
4562+ # Finally convert to offset units specified in destination
4563+ for u, e in dst_offset_units:
4564+ value = self._units[u].converter.from_reference(value, inplace)
4565+ # add back offset units to dst
4566+ dst[u] = e
4567+
4568+ # restore offset conversion of src units
4569+ for u, e in src_offset_units:
4570+ src[u] = e
4571+
4572 return value
4573
4574 def pi_theorem(self, quantities):
4575@@ -1069,9 +1145,10 @@
4576 """Parse a unit to identify prefix, unit name and suffix
4577 by walking the list of prefix and suffix.
4578 """
4579-
4580- for suffix, prefix in itertools.product(self._suffixes.keys(), self._prefixes.keys()):
4581- if unit_name.startswith(prefix) and unit_name.endswith(suffix):
4582+ stw = unit_name.startswith
4583+ edw = unit_name.endswith
4584+ for suffix, prefix in itertools.product(self._suffixes, self._prefixes):
4585+ if stw(prefix) and edw(suffix):
4586 name = unit_name[len(prefix):]
4587 if suffix:
4588 name = name[:-len(suffix)]
4589@@ -1079,30 +1156,33 @@
4590 continue
4591 if case_sensitive:
4592 if name in self._units:
4593- yield (self._prefixes[prefix].name,
4594- self._units[name].name,
4595+ yield (self._prefixes[prefix]._name,
4596+ self._units[name]._name,
4597 self._suffixes[suffix])
4598 else:
4599 for real_name in self._units_casei.get(name.lower(), ()):
4600- yield (self._prefixes[prefix].name,
4601- self._units[real_name].name,
4602+ yield (self._prefixes[prefix]._name,
4603+ self._units[real_name]._name,
4604 self._suffixes[suffix])
4605
4606- def parse_units(self, input_string, to_delta=None):
4607+ def parse_units(self, input_string, as_delta=None):
4608 """Parse a units expression and returns a UnitContainer with
4609 the canonical names.
4610
4611 The expression can only contain products, ratios and powers of units.
4612
4613- :param to_delta: if the expression has multiple units, the parser will
4614+ :param as_delta: if the expression has multiple units, the parser will
4615 interpret non multiplicative units as their `delta_` counterparts.
4616
4617 :raises:
4618 :class:`pint.UndefinedUnitError` if a unit is not in the registry
4619 :class:`ValueError` if the expression is invalid.
4620 """
4621- if to_delta is None:
4622- to_delta = self.default_to_delta
4623+ if input_string in self._parse_unit_cache:
4624+ return self._parse_unit_cache[input_string]
4625+
4626+ if as_delta is None:
4627+ as_delta = self.default_as_delta
4628
4629 if not input_string:
4630 return UnitsContainer()
4631@@ -1113,16 +1193,19 @@
4632
4633 ret = UnitsContainer()
4634 many = len(units) > 1
4635- for name, value in units.items():
4636+ for name in units:
4637 cname = self.get_name(name)
4638+ value = units[name]
4639 if not cname:
4640 continue
4641- if to_delta and (many or (not many and abs(value) != 1)):
4642+ if as_delta and (many or (not many and value != 1)):
4643 definition = self._units[cname]
4644 if not definition.is_multiplicative:
4645 cname = 'delta_' + cname
4646 ret[cname] = value
4647
4648+ self._parse_unit_cache[input_string] = ret
4649+
4650 return ret
4651
4652 def parse_expression(self, input_string, case_sensitive=True, **values):
4653@@ -1141,7 +1224,7 @@
4654 unknown = set()
4655 for toknum, tokval, _, _, _ in gen:
4656 if toknum == NAME:
4657- # TODO: Integrate math better, Replace eval
4658+ # TODO: Integrate math better, Replace eval, make as_delta-aware
4659 if tokval == 'pi' or tokval in values:
4660 result.append((toknum, tokval))
4661 continue
4662
4663=== modified file 'setup.cfg'
4664--- setup.cfg 2014-09-05 23:26:05 +0000
4665+++ setup.cfg 2015-01-07 15:51:38 +0000
4666@@ -1,7 +1,13 @@
4667 [check-manifest]
4668-ignore =
4669- .travis.yml
4670- tox.ini
4671+ignore =
4672+ .travis.yml
4673+ tox.ini
4674
4675 [bdist_wheel]
4676 universal = 1
4677+
4678+[egg_info]
4679+tag_build =
4680+tag_date = 0
4681+tag_svn_revision = 0
4682+
4683
4684=== modified file 'setup.py'
4685--- setup.py 2014-09-05 23:26:05 +0000
4686+++ setup.py 2015-01-07 15:51:38 +0000
4687@@ -29,7 +29,7 @@
4688
4689 setup(
4690 name='Pint',
4691- version='0.5.2',
4692+ version='0.6',
4693 description='Physical quantities module',
4694 long_description=long_description,
4695 keywords='physical quantities unit conversion science',
4696
4697=== removed file 'tox.ini'
4698--- tox.ini 2014-09-05 23:26:05 +0000
4699+++ tox.ini 1970-01-01 00:00:00 +0000
4700@@ -1,77 +0,0 @@
4701-[tox]
4702-envlist = py26,py27,py32,py33,numpy26,numpy27,numpy32,numpy33,
4703- py26u,py27u,py32u,py33u,numpy26u,numpy27u,numpy32u,numpy33u
4704-
4705-[testenv]
4706-deps = coverage
4707-commands = coverage run -p --source=pint setup.py test
4708-
4709-setenv=
4710- COVERAGE_FILE=.coverage.{envname}
4711-
4712-
4713-[testenv:py26]
4714-deps = unittest2
4715- coverage
4716-
4717-[testenv:numpy26]
4718-deps = numpy
4719- unittest2
4720- coverage
4721-
4722-[testenv:numpy27]
4723-basepython=python2.7
4724-deps = numpy
4725- coverage
4726-
4727-[testenv:numpy32]
4728-basepython=python3.2
4729-deps = numpy
4730- coverage
4731-
4732-[testenv:numpy33]
4733-basepython=python3.3
4734-deps = numpy
4735- coverage
4736-
4737-
4738-[testenv:py26u]
4739-deps = unittest2
4740- uncertainties
4741- coverage
4742-
4743-[testenv:py27u]
4744-deps = uncertainties
4745- coverage
4746-
4747-[testenv:py32u]
4748-deps = uncertainties
4749- coverage
4750-
4751-[testenv:py33u]
4752-deps = uncertainties
4753- coverage
4754-
4755-[testenv:numpy26u]
4756-deps = numpy
4757- unittest2
4758- uncertainties
4759- coverage
4760-
4761-[testenv:numpy27u]
4762-basepython=python2.7
4763-deps = numpy
4764- uncertainties
4765- coverage
4766-
4767-[testenv:numpy32u]
4768-basepython=python3.2
4769-deps = numpy
4770- uncertainties
4771- coverage
4772-
4773-[testenv:numpy33u]
4774-basepython=python3.3
4775-deps = numpy
4776- uncertainties
4777- coverage

Subscribers

People subscribed via source and target branches

to all changes: