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
=== removed file '.gitignore'
--- .gitignore 2014-09-05 23:26:05 +0000
+++ .gitignore 1970-01-01 00:00:00 +0000
@@ -1,14 +0,0 @@
1*~
2__pycache__
3*egg-info*
4*.pyc
5.DS_Store
6docs/_build/
7.idea
8build/
9dist/
10MANIFEST
11.tox
12
13# WebDAV file system cache files
14.DAV/
150
=== removed file '.pc/applied-patches'
--- .pc/applied-patches 2014-09-05 23:26:05 +0000
+++ .pc/applied-patches 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
1no-intersphinx.patch
2removes-privacy-breach-in-doc.patch
30
=== removed directory '.pc/no-intersphinx.patch'
=== removed directory '.pc/no-intersphinx.patch/docs'
=== removed file '.pc/no-intersphinx.patch/docs/conf.py'
--- .pc/no-intersphinx.patch/docs/conf.py 2014-09-05 23:26:05 +0000
+++ .pc/no-intersphinx.patch/docs/conf.py 1970-01-01 00:00:00 +0000
@@ -1,299 +0,0 @@
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# pint documentation build configuration file, created by
5# sphinx-quickstart on Thu Mar 1 13:33:14 2012.
6#
7# This file is execfile()d with the current directory set to its containing dir.
8#
9# Note that not all possible configuration values are present in this
10# autogenerated file.
11#
12# All configuration values have a default; values that are commented out
13# serve to show the default.
14
15import sys, os
16import pkg_resources
17import datetime
18
19# If extensions (or modules to document with autodoc) are in another directory,
20# add these directories to sys.path here. If the directory is relative to the
21# documentation root, use os.path.abspath to make it absolute, like shown here.
22#sys.path.insert(0, os.path.abspath('.'))
23
24# -- General configuration -----------------------------------------------------
25
26# If your documentation needs a minimal Sphinx version, state it here.
27#needs_sphinx = '1.0'
28
29# Add any Sphinx extension module names here, as strings. They can be extensions
30# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
31extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax']
32
33# Add any paths that contain templates here, relative to this directory.
34templates_path = ['_templates']
35
36# The suffix of source filenames.
37source_suffix = '.rst'
38
39# The encoding of source files.
40#source_encoding = 'utf-8-sig'
41
42# The master toctree document.
43master_doc = 'index'
44
45# General information about the project.
46project = 'pint'
47author = 'Hernan E. Grecco'
48
49# The version info for the project you're documenting, acts as replacement for
50# |version| and |release|, also used in various other places throughout the
51# built documents.
52
53version = pkg_resources.get_distribution(project).version
54release = version
55this_year = datetime.date.today().year
56copyright = '%s, %s' % (this_year, author)
57
58# The language for content autogenerated by Sphinx. Refer to documentation
59# for a list of supported languages.
60#language = None
61
62# There are two options for replacing |today|: either, you set today to some
63# non-false value, then it is used:
64#today = ''
65# Else, today_fmt is used as the format for a strftime call.
66#today_fmt = '%B %d, %Y'
67
68# List of patterns, relative to source directory, that match files and
69# directories to ignore when looking for source files.
70exclude_patterns = ['_build']
71
72# The reST default role (used for this markup: `text`) to use for all documents.
73#default_role = None
74
75# If true, '()' will be appended to :func: etc. cross-reference text.
76#add_function_parentheses = True
77
78# If true, the current module name will be prepended to all description
79# unit titles (such as .. function::).
80#add_module_names = True
81
82# If true, sectionauthor and moduleauthor directives will be shown in the
83# output. They are ignored by default.
84#show_authors = False
85
86# The name of the Pygments (syntax highlighting) style to use.
87pygments_style = 'sphinx'
88
89# A list of ignored prefixes for module index sorting.
90#modindex_common_prefix = []
91
92
93# -- Options for HTML output ---------------------------------------------------
94
95# The theme to use for HTML and HTML Help pages. See the documentation for
96# a list of builtin themes.
97#html_theme = 'default'
98html_theme = 'flask'
99
100# Theme options are theme-specific and customize the look and feel of a theme
101# further. For a list of options available for each theme, see the
102# documentation.
103#html_theme_options = {}
104
105# Add any paths that contain custom themes here, relative to this directory.
106#html_theme_path = []
107html_theme_path = ['_themes']
108
109# The name for this set of Sphinx documents. If None, it defaults to
110# "<project> v<release> documentation".
111#html_title = None
112
113# A shorter title for the navigation bar. Default is the same as html_title.
114#html_short_title = None
115
116# The name of an image file (relative to this directory) to place at the top
117# of the sidebar.
118#html_logo = None
119
120# The name of an image file (within the static path) to use as favicon of the
121# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
122# pixels large.
123#html_favicon = None
124
125# Add any paths that contain custom static files (such as style sheets) here,
126# relative to this directory. They are copied after the builtin static files,
127# so a file named "default.css" will overwrite the builtin "default.css".
128html_static_path = ['_static']
129
130# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
131# using the given strftime format.
132#html_last_updated_fmt = '%b %d, %Y'
133
134# If true, SmartyPants will be used to convert quotes and dashes to
135# typographically correct entities.
136#html_use_smartypants = True
137
138# Custom sidebar templates, maps document names to template names.
139#html_sidebars = {}
140html_sidebars = {
141 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
142 '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
143 'sourcelink.html', 'searchbox.html']
144}
145
146# Additional templates that should be rendered to pages, maps page names to
147# template names.
148#html_additional_pages = {}
149
150# If false, no module index is generated.
151#html_domain_indices = True
152
153# If false, no index is generated.
154#html_use_index = True
155
156# If true, the index is split into individual pages for each letter.
157#html_split_index = False
158
159# If true, links to the reST sources are added to the pages.
160#html_show_sourcelink = True
161
162# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
163#html_show_sphinx = True
164
165# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
166#html_show_copyright = True
167
168# If true, an OpenSearch description file will be output, and all pages will
169# contain a <link> tag referring to it. The value of this option must be the
170# base URL from which the finished HTML is served.
171#html_use_opensearch = ''
172
173# This is the file name suffix for HTML files (e.g. ".xhtml").
174#html_file_suffix = None
175
176# Output file base name for HTML help builder.
177htmlhelp_basename = 'pintdoc'
178
179
180# -- Options for LaTeX output --------------------------------------------------
181
182latex_elements = {
183# The paper size ('letterpaper' or 'a4paper').
184#'papersize': 'letterpaper',
185
186# The font size ('10pt', '11pt' or '12pt').
187#'pointsize': '10pt',
188
189# Additional stuff for the LaTeX preamble.
190#'preamble': '',
191}
192
193# Grouping the document tree into LaTeX files. List of tuples
194# (source start file, target name, title, author, documentclass [howto/manual]).
195latex_documents = [
196 ('index', 'pint.tex', 'pint Documentation',
197 'Hernan E. Grecco', 'manual'),
198]
199
200# The name of an image file (relative to this directory) to place at the top of
201# the title page.
202#latex_logo = None
203
204# For "manual" documents, if this is true, then toplevel headings are parts,
205# not chapters.
206#latex_use_parts = False
207
208# If true, show page references after internal links.
209#latex_show_pagerefs = False
210
211# If true, show URL addresses after external links.
212#latex_show_urls = False
213
214# Documents to append as an appendix to all manuals.
215#latex_appendices = []
216
217# If false, no module index is generated.
218#latex_domain_indices = True
219
220
221# -- Options for manual page output --------------------------------------------
222
223# One entry per manual page. List of tuples
224# (source start file, name, description, authors, manual section).
225man_pages = [
226 ('index', 'pint', 'pint Documentation',
227 ['Hernan E. Grecco'], 1)
228]
229
230# If true, show URL addresses after external links.
231#man_show_urls = False
232
233
234# -- Options for Texinfo output ------------------------------------------------
235
236# Grouping the document tree into Texinfo files. List of tuples
237# (source start file, target name, title, author,
238# dir menu entry, description, category)
239texinfo_documents = [
240 ('index', 'pint', 'pint Documentation',
241 'Hernan E. Grecco', 'pint', 'One line description of project.',
242 'Miscellaneous'),
243]
244
245# Documents to append as an appendix to all manuals.
246#texinfo_appendices = []
247
248# If false, no module index is generated.
249#texinfo_domain_indices = True
250
251# How to display URL addresses: 'footnote', 'no', or 'inline'.
252#texinfo_show_urls = 'footnote'
253
254
255# -- Options for Epub output ---------------------------------------------------
256
257# Bibliographic Dublin Core info.
258epub_title = project
259epub_author = author
260epub_publisher = author
261epub_copyright = copyright
262
263# The language of the text. It defaults to the language option
264# or en if the language is not set.
265#epub_language = ''
266
267# The scheme of the identifier. Typical schemes are ISBN or URL.
268#epub_scheme = ''
269
270# The unique identifier of the text. This can be a ISBN number
271# or the project homepage.
272#epub_identifier = ''
273
274# A unique identification for the text.
275#epub_uid = ''
276
277# A tuple containing the cover image and cover page html template filenames.
278#epub_cover = ()
279
280# HTML files that should be inserted before the pages created by sphinx.
281# The format is a list of tuples containing the path and title.
282#epub_pre_files = []
283
284# HTML files shat should be inserted after the pages created by sphinx.
285# The format is a list of tuples containing the path and title.
286#epub_post_files = []
287
288# A list of files that should not be packed into the epub file.
289#epub_exclude_files = []
290
291# The depth of the table of contents in toc.ncx.
292#epub_tocdepth = 3
293
294# Allow duplicate toc entries.
295#epub_tocdup = True
296
297
298# Example configuration for intersphinx: refer to the Python standard library.
299intersphinx_mapping = {'http://docs.python.org/': None}
3000
=== removed directory '.pc/removes-privacy-breach-in-doc.patch'
=== removed directory '.pc/removes-privacy-breach-in-doc.patch/docs'
=== removed directory '.pc/removes-privacy-breach-in-doc.patch/docs/_themes'
=== removed directory '.pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask'
=== removed file '.pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask/layout.html'
--- .pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask/layout.html 2014-09-05 23:26:05 +0000
+++ .pc/removes-privacy-breach-in-doc.patch/docs/_themes/flask/layout.html 1970-01-01 00:00:00 +0000
@@ -1,30 +0,0 @@
1{%- extends "basic/layout.html" %}
2{%- block extrahead %}
3 {{ super() }}
4 {% if theme_touch_icon %}
5 <link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
6 {% endif %}
7 <link media="only screen and (max-device-width: 480px)" href="{{
8 pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
9{% endblock %}
10{%- block relbar2 %}
11 {% if theme_github_fork %}
12 <a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
13 src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
14 {% endif %}
15{% endblock %}
16{% block header %}
17 {{ super() }}
18 {% if pagename == 'index' %}
19 <div>
20 {% endif %}
21{% endblock %}
22{%- block footer %}
23 <div class="footer">
24 &copy; Copyright {{ copyright }}. Pint {{ version }}.
25 Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
26 </div>
27 {% if pagename == 'index' %}
28 </div>
29 {% endif %}
30{%- endblock %}
310
=== removed file '.pc/removes-privacy-breach-in-doc.patch/docs/conf.py'
--- .pc/removes-privacy-breach-in-doc.patch/docs/conf.py 2014-09-05 23:26:05 +0000
+++ .pc/removes-privacy-breach-in-doc.patch/docs/conf.py 1970-01-01 00:00:00 +0000
@@ -1,299 +0,0 @@
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# pint documentation build configuration file, created by
5# sphinx-quickstart on Thu Mar 1 13:33:14 2012.
6#
7# This file is execfile()d with the current directory set to its containing dir.
8#
9# Note that not all possible configuration values are present in this
10# autogenerated file.
11#
12# All configuration values have a default; values that are commented out
13# serve to show the default.
14
15import sys, os
16import pkg_resources
17import datetime
18
19# If extensions (or modules to document with autodoc) are in another directory,
20# add these directories to sys.path here. If the directory is relative to the
21# documentation root, use os.path.abspath to make it absolute, like shown here.
22#sys.path.insert(0, os.path.abspath('.'))
23
24# -- General configuration -----------------------------------------------------
25
26# If your documentation needs a minimal Sphinx version, state it here.
27#needs_sphinx = '1.0'
28
29# Add any Sphinx extension module names here, as strings. They can be extensions
30# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
31extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax']
32
33# Add any paths that contain templates here, relative to this directory.
34templates_path = ['_templates']
35
36# The suffix of source filenames.
37source_suffix = '.rst'
38
39# The encoding of source files.
40#source_encoding = 'utf-8-sig'
41
42# The master toctree document.
43master_doc = 'index'
44
45# General information about the project.
46project = 'pint'
47author = 'Hernan E. Grecco'
48
49# The version info for the project you're documenting, acts as replacement for
50# |version| and |release|, also used in various other places throughout the
51# built documents.
52
53version = pkg_resources.get_distribution(project).version
54release = version
55this_year = datetime.date.today().year
56copyright = '%s, %s' % (this_year, author)
57
58# The language for content autogenerated by Sphinx. Refer to documentation
59# for a list of supported languages.
60#language = None
61
62# There are two options for replacing |today|: either, you set today to some
63# non-false value, then it is used:
64#today = ''
65# Else, today_fmt is used as the format for a strftime call.
66#today_fmt = '%B %d, %Y'
67
68# List of patterns, relative to source directory, that match files and
69# directories to ignore when looking for source files.
70exclude_patterns = ['_build']
71
72# The reST default role (used for this markup: `text`) to use for all documents.
73#default_role = None
74
75# If true, '()' will be appended to :func: etc. cross-reference text.
76#add_function_parentheses = True
77
78# If true, the current module name will be prepended to all description
79# unit titles (such as .. function::).
80#add_module_names = True
81
82# If true, sectionauthor and moduleauthor directives will be shown in the
83# output. They are ignored by default.
84#show_authors = False
85
86# The name of the Pygments (syntax highlighting) style to use.
87pygments_style = 'sphinx'
88
89# A list of ignored prefixes for module index sorting.
90#modindex_common_prefix = []
91
92
93# -- Options for HTML output ---------------------------------------------------
94
95# The theme to use for HTML and HTML Help pages. See the documentation for
96# a list of builtin themes.
97#html_theme = 'default'
98html_theme = 'flask'
99
100# Theme options are theme-specific and customize the look and feel of a theme
101# further. For a list of options available for each theme, see the
102# documentation.
103#html_theme_options = {}
104
105# Add any paths that contain custom themes here, relative to this directory.
106#html_theme_path = []
107html_theme_path = ['_themes']
108
109# The name for this set of Sphinx documents. If None, it defaults to
110# "<project> v<release> documentation".
111#html_title = None
112
113# A shorter title for the navigation bar. Default is the same as html_title.
114#html_short_title = None
115
116# The name of an image file (relative to this directory) to place at the top
117# of the sidebar.
118#html_logo = None
119
120# The name of an image file (within the static path) to use as favicon of the
121# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
122# pixels large.
123#html_favicon = None
124
125# Add any paths that contain custom static files (such as style sheets) here,
126# relative to this directory. They are copied after the builtin static files,
127# so a file named "default.css" will overwrite the builtin "default.css".
128html_static_path = ['_static']
129
130# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
131# using the given strftime format.
132#html_last_updated_fmt = '%b %d, %Y'
133
134# If true, SmartyPants will be used to convert quotes and dashes to
135# typographically correct entities.
136#html_use_smartypants = True
137
138# Custom sidebar templates, maps document names to template names.
139#html_sidebars = {}
140html_sidebars = {
141 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
142 '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
143 'sourcelink.html', 'searchbox.html']
144}
145
146# Additional templates that should be rendered to pages, maps page names to
147# template names.
148#html_additional_pages = {}
149
150# If false, no module index is generated.
151#html_domain_indices = True
152
153# If false, no index is generated.
154#html_use_index = True
155
156# If true, the index is split into individual pages for each letter.
157#html_split_index = False
158
159# If true, links to the reST sources are added to the pages.
160#html_show_sourcelink = True
161
162# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
163#html_show_sphinx = True
164
165# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
166#html_show_copyright = True
167
168# If true, an OpenSearch description file will be output, and all pages will
169# contain a <link> tag referring to it. The value of this option must be the
170# base URL from which the finished HTML is served.
171#html_use_opensearch = ''
172
173# This is the file name suffix for HTML files (e.g. ".xhtml").
174#html_file_suffix = None
175
176# Output file base name for HTML help builder.
177htmlhelp_basename = 'pintdoc'
178
179
180# -- Options for LaTeX output --------------------------------------------------
181
182latex_elements = {
183# The paper size ('letterpaper' or 'a4paper').
184#'papersize': 'letterpaper',
185
186# The font size ('10pt', '11pt' or '12pt').
187#'pointsize': '10pt',
188
189# Additional stuff for the LaTeX preamble.
190#'preamble': '',
191}
192
193# Grouping the document tree into LaTeX files. List of tuples
194# (source start file, target name, title, author, documentclass [howto/manual]).
195latex_documents = [
196 ('index', 'pint.tex', 'pint Documentation',
197 'Hernan E. Grecco', 'manual'),
198]
199
200# The name of an image file (relative to this directory) to place at the top of
201# the title page.
202#latex_logo = None
203
204# For "manual" documents, if this is true, then toplevel headings are parts,
205# not chapters.
206#latex_use_parts = False
207
208# If true, show page references after internal links.
209#latex_show_pagerefs = False
210
211# If true, show URL addresses after external links.
212#latex_show_urls = False
213
214# Documents to append as an appendix to all manuals.
215#latex_appendices = []
216
217# If false, no module index is generated.
218#latex_domain_indices = True
219
220
221# -- Options for manual page output --------------------------------------------
222
223# One entry per manual page. List of tuples
224# (source start file, name, description, authors, manual section).
225man_pages = [
226 ('index', 'pint', 'pint Documentation',
227 ['Hernan E. Grecco'], 1)
228]
229
230# If true, show URL addresses after external links.
231#man_show_urls = False
232
233
234# -- Options for Texinfo output ------------------------------------------------
235
236# Grouping the document tree into Texinfo files. List of tuples
237# (source start file, target name, title, author,
238# dir menu entry, description, category)
239texinfo_documents = [
240 ('index', 'pint', 'pint Documentation',
241 'Hernan E. Grecco', 'pint', 'One line description of project.',
242 'Miscellaneous'),
243]
244
245# Documents to append as an appendix to all manuals.
246#texinfo_appendices = []
247
248# If false, no module index is generated.
249#texinfo_domain_indices = True
250
251# How to display URL addresses: 'footnote', 'no', or 'inline'.
252#texinfo_show_urls = 'footnote'
253
254
255# -- Options for Epub output ---------------------------------------------------
256
257# Bibliographic Dublin Core info.
258epub_title = project
259epub_author = author
260epub_publisher = author
261epub_copyright = copyright
262
263# The language of the text. It defaults to the language option
264# or en if the language is not set.
265#epub_language = ''
266
267# The scheme of the identifier. Typical schemes are ISBN or URL.
268#epub_scheme = ''
269
270# The unique identifier of the text. This can be a ISBN number
271# or the project homepage.
272#epub_identifier = ''
273
274# A unique identification for the text.
275#epub_uid = ''
276
277# A tuple containing the cover image and cover page html template filenames.
278#epub_cover = ()
279
280# HTML files that should be inserted before the pages created by sphinx.
281# The format is a list of tuples containing the path and title.
282#epub_pre_files = []
283
284# HTML files shat should be inserted after the pages created by sphinx.
285# The format is a list of tuples containing the path and title.
286#epub_post_files = []
287
288# A list of files that should not be packed into the epub file.
289#epub_exclude_files = []
290
291# The depth of the table of contents in toc.ncx.
292#epub_tocdepth = 3
293
294# Allow duplicate toc entries.
295#epub_tocdup = True
296
297
298# Example configuration for intersphinx: refer to the Python standard library.
299intersphinx_mapping = {'http://docs.python.org/': None}
3000
=== removed file '.travis.yml'
--- .travis.yml 2014-09-05 23:26:05 +0000
+++ .travis.yml 1970-01-01 00:00:00 +0000
@@ -1,65 +0,0 @@
1language: python
2
3python:
4 - "2.6"
5 - "2.7"
6 - "3.2"
7 - "3.3"
8 - "3.4"
9
10env:
11 - UNCERTAINTIES="N" NUMPY_VERSION=0
12 - UNCERTAINTIES="N" NUMPY_VERSION=1.6.2
13 - UNCERTAINTIES="N" NUMPY_VERSION=1.7.1
14 - UNCERTAINTIES="N" NUMPY_VERSION=1.8.1
15 - UNCERTAINTIES="Y" NUMPY_VERSION=0
16 - UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2
17 - UNCERTAINTIES="Y" NUMPY_VERSION=1.7.1
18 - UNCERTAINTIES="Y" NUMPY_VERSION=1.8.1
19
20branches:
21 only:
22 - master
23 - develop
24
25before_install:
26 - wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
27 - chmod +x miniconda.sh
28 - ./miniconda.sh -b
29 - export PATH=/home/travis/miniconda/bin:$PATH
30 - conda update --yes conda
31
32 # The next couple lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda
33 - sudo rm -rf /dev/shm
34 - sudo ln -s /run/shm /dev/shm
35
36install:
37 - conda create -c mwcraig --yes -n env_name python=$TRAVIS_PYTHON_VERSION pip
38 - source activate env_name
39 - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then pip install unittest2; fi
40 - if [ $UNCERTAINTIES == 'Y' ]; then pip install uncertainties; fi
41 - if [ $NUMPY_VERSION != '0' ]; then conda install -c mwcraig --yes numpy==$NUMPY_VERSION; fi
42 - pip install coverage coveralls
43
44script:
45 - coverage run -p --source=pint --omit="*test*","*compat*" setup.py test
46 - coverage combine
47 - coverage report -m
48
49after_script:
50 - coveralls --verbose
51
52matrix:
53 exclude:
54 - python: "3.3"
55 env: UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2
56 - python: "3.4"
57 env: UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2
58 - python: "3.4"
59 env: UNCERTAINTIES="Y" NUMPY_VERSION=1.7.1
60 - python: "3.3"
61 env: UNCERTAINTIES="N" NUMPY_VERSION=1.6.2
62 - python: "3.4"
63 env: UNCERTAINTIES="N" NUMPY_VERSION=1.6.2
64 - python: "3.4"
65 env: UNCERTAINTIES="N" NUMPY_VERSION=1.7.1
660
=== modified file 'AUTHORS'
--- AUTHORS 2014-09-05 23:26:05 +0000
+++ AUTHORS 2015-01-07 15:51:38 +0000
@@ -5,9 +5,9 @@
5* Alexander Böhn <fish2000@gmail.com>5* Alexander Böhn <fish2000@gmail.com>
6* Brend Wanders <b.wanders@utwente.nl>6* Brend Wanders <b.wanders@utwente.nl>
7* choloepus7* choloepus
8* coutinho <coutinho@esrf.fr>
9* Daniel Sokolowski <daniel.sokolowski@danols.com>8* Daniel Sokolowski <daniel.sokolowski@danols.com>
10* Dave Brooks <dave@bcs.co.nz>9* Dave Brooks <dave@bcs.co.nz>
10* David Linke
11* Eduard Bopp <eduard.bopp@aepsil0n.de>11* Eduard Bopp <eduard.bopp@aepsil0n.de>
12* Felix Hummel <felix@felixhummel.de>12* Felix Hummel <felix@felixhummel.de>
13* Giel van Schijndel <me@mortis.eu>13* Giel van Schijndel <me@mortis.eu>
@@ -15,11 +15,17 @@
15* Jim Turner <jturner314@gmail.com>15* Jim Turner <jturner314@gmail.com>
16* Joel B. Mohler <joel@kiwistrawberry.us>16* Joel B. Mohler <joel@kiwistrawberry.us>
17* John David Reaver <jdreaver@adlerhorst.com>17* John David Reaver <jdreaver@adlerhorst.com>
18* Jonas Olson <jolson@kth.se>
18* Kenneth D. Mankoff <mankoff@gmail.com>19* Kenneth D. Mankoff <mankoff@gmail.com>
19* Luke Campbell <luke.s.campbell@gmail.com>20* Luke Campbell <luke.s.campbell@gmail.com>
21* Matthieu Dartiailh <marul@laposte.net>
20* Nate Bogdanowicz <natezb@gmail.com>22* Nate Bogdanowicz <natezb@gmail.com>
21* Peter Grayson <jpgrayson@gmail.com>23* Peter Grayson <jpgrayson@gmail.com>
22* Richard Barnes <rbarnes@umn.edu>24* Richard Barnes <rbarnes@umn.edu>
25* Ryan Kingsbury <RyanSKingsbury@alumni.unc.edu>
23* Sundar Raman <cybertoast@gmail.com>26* Sundar Raman <cybertoast@gmail.com>
24* Tiago Coutinho <coutinho@esrf.fr>27* Tiago Coutinho <coutinho@esrf.fr>
25* Tom Ritchford <tom@swirly.com>28* Tom Ritchford <tom@swirly.com>
29* Virgil Dupras <virgil.dupras@savoirfairelinux.com>
30
31(If you think that your name belongs here, please let the maintainer know)
2632
=== modified file 'CHANGES'
--- CHANGES 2014-09-05 23:26:05 +0000
+++ CHANGES 2015-01-07 15:51:38 +0000
@@ -2,6 +2,39 @@
2==============2==============
33
44
50.6 (2014-11-07)
6----------------
7
8- Fix operations with measurments and user defined units.
9 (Issue #204)
10- Faster conversions through caching and other performance improvements.
11 (Issue #193, thanks MatthieuDartiailh)
12- Better error messages on Quantity.__setitem__.
13 (Issue #191)
14- Fixed abbreviation of fluid_ounce.
15 (Issue #187, thanks hsoft)
16- Defined Angstrom symbol.
17 (Issue #181, thanks JonasOlson)
18- Removed fetching version from git repo as it triggers XCode installation on OSX.
19 (Issue #178, thanks deanishe)
20- Improved context documentation.
21 (Issue #176 and 179, thanks rsking84)
22- Added Chemistry context.
23 (Issue #179, thanks rsking84)
24- Fix help(UnitRegisty)
25 (Issue #168)
26- Optimized "get_dimensionality" and "get_base_name".
27 (Issue #166 and #167, thanks jbmohler)
28- Renamed ureg.parse_units parameter "to_delta" to "as_delta" to make clear.
29 that no conversion happens. Accordingly, the parameter/property
30 "default_to_delta" of UnitRegistry was renamed to "default_as_delta".
31 (Issue #158, thanks dalit)
32- Fixed problem when adding two uncertainties.
33 (thanks dalito)
34- Full support for Offset units (e.g. temperature)
35 (Issue #88, #143, #147 and #161, thanks dalito)
36
37
50.5.2 (2014-07-31)380.5.2 (2014-07-31)
6------------------39------------------
740
841
=== added file 'CHANGES_DEV'
--- CHANGES_DEV 1970-01-01 00:00:00 +0000
+++ CHANGES_DEV 2015-01-07 15:51:38 +0000
@@ -0,0 +1,5 @@
1
2
3
40.7 (unreleased)
5----------------
06
=== modified file 'MANIFEST.in'
--- MANIFEST.in 2014-09-05 23:26:05 +0000
+++ MANIFEST.in 2015-01-07 15:51:38 +0000
@@ -1,4 +1,4 @@
1include README AUTHORS CHANGES LICENSE1include README AUTHORS CHANGES LICENSE CHANGES_DEV
2recursive-include pint *2recursive-include pint *
3recursive-include docs *3recursive-include docs *
4recursive-include bench *4recursive-include bench *
55
=== added file 'PKG-INFO'
--- PKG-INFO 1970-01-01 00:00:00 +0000
+++ PKG-INFO 2015-01-07 15:51:38 +0000
@@ -0,0 +1,400 @@
1Metadata-Version: 1.1
2Name: Pint
3Version: 0.6
4Summary: Physical quantities module
5Home-page: https://github.com/hgrecco/pint
6Author: Hernan E. Grecco
7Author-email: hernan.grecco@gmail.com
8License: BSD
9Description: Pint: a Python units library
10 ============================
11
12 Pint is Python module/package to define, operate and manipulate physical
13 quantities: the product of a numerical value and a unit of measurement.
14 It allows arithmetic operations between them and conversions from and
15 to different units.
16
17 It is distributed with a comprehensive list of physical units, prefixes
18 and constants. Due to its modular design, you to extend (or even rewrite!)
19 the complete list without changing the source code.
20
21 It has a complete test coverage. It runs in Python 2.7 and 3.X
22 with no other dependency. It licensed under BSD.
23
24
25 Design principles
26 -----------------
27
28 Although there are already a few very good Python packages to handle physical
29 quantities, no one was really fitting my needs. Like most developers, I programed
30 Pint to scratch my own itches.
31
32 - Unit parsing: prefixed and pluralized forms of units are recognized without
33 explicitly defining them. In other words: as the prefix *kilo* and the unit *meter*
34 are defined, Pint understands *kilometers*. This results in a much shorter and
35 maintainable unit definition list as compared to other packages.
36
37 - Standalone unit definitions: units definitions are loaded from simple and
38 easy to edit text file. Adding and changing units and their definitions does
39 not involve changing the code.
40
41 - Advanced string formatting: a quantity can be formatted into string using
42 PEP 3101 syntax. Extended conversion flags are given to provide latex and pretty
43 formatting.
44
45 - Small codebase: small and easy to maintain with a flat hierarchy.
46
47 - Dependency free: it depends only on Python and its standard library.
48
49 - Python 2 and 3: A single codebase that runs unchanged in Python 2.7+ and Python 3.0+.
50
51 - Advanced NumPy support: While NumPy is not a requirement for Pint,
52 when available ndarray methods and ufuncs can be used in Quantity objects.
53
54
55 Pint is written and maintained by Hernan E. Grecco <hernan.grecco@gmail.com>.
56
57 Other contributors, listed alphabetically, are:
58
59 * Alexander Böhn <fish2000@gmail.com>
60 * Brend Wanders <b.wanders@utwente.nl>
61 * choloepus
62 * Daniel Sokolowski <daniel.sokolowski@danols.com>
63 * Dave Brooks <dave@bcs.co.nz>
64 * David Linke
65 * Eduard Bopp <eduard.bopp@aepsil0n.de>
66 * Felix Hummel <felix@felixhummel.de>
67 * Giel van Schijndel <me@mortis.eu>
68 * James Rowe <jnrowe@gmail.com>
69 * Jim Turner <jturner314@gmail.com>
70 * Joel B. Mohler <joel@kiwistrawberry.us>
71 * John David Reaver <jdreaver@adlerhorst.com>
72 * Jonas Olson <jolson@kth.se>
73 * Kenneth D. Mankoff <mankoff@gmail.com>
74 * Luke Campbell <luke.s.campbell@gmail.com>
75 * Matthieu Dartiailh <marul@laposte.net>
76 * Nate Bogdanowicz <natezb@gmail.com>
77 * Peter Grayson <jpgrayson@gmail.com>
78 * Richard Barnes <rbarnes@umn.edu>
79 * Ryan Kingsbury <RyanSKingsbury@alumni.unc.edu>
80 * Sundar Raman <cybertoast@gmail.com>
81 * Tiago Coutinho <coutinho@esrf.fr>
82 * Tom Ritchford <tom@swirly.com>
83 * Virgil Dupras <virgil.dupras@savoirfairelinux.com>
84
85 (If you think that your name belongs here, please let the maintainer know)
86
87
88 Pint Changelog
89 ==============
90
91
92 0.6 (2014-11-07)
93 ----------------
94
95 - Fix operations with measurments and user defined units.
96 (Issue #204)
97 - Faster conversions through caching and other performance improvements.
98 (Issue #193, thanks MatthieuDartiailh)
99 - Better error messages on Quantity.__setitem__.
100 (Issue #191)
101 - Fixed abbreviation of fluid_ounce.
102 (Issue #187, thanks hsoft)
103 - Defined Angstrom symbol.
104 (Issue #181, thanks JonasOlson)
105 - Removed fetching version from git repo as it triggers XCode installation on OSX.
106 (Issue #178, thanks deanishe)
107 - Improved context documentation.
108 (Issue #176 and 179, thanks rsking84)
109 - Added Chemistry context.
110 (Issue #179, thanks rsking84)
111 - Fix help(UnitRegisty)
112 (Issue #168)
113 - Optimized "get_dimensionality" and "get_base_name".
114 (Issue #166 and #167, thanks jbmohler)
115 - Renamed ureg.parse_units parameter "to_delta" to "as_delta" to make clear.
116 that no conversion happens. Accordingly, the parameter/property
117 "default_to_delta" of UnitRegistry was renamed to "default_as_delta".
118 (Issue #158, thanks dalit)
119 - Fixed problem when adding two uncertainties.
120 (thanks dalito)
121 - Full support for Offset units (e.g. temperature)
122 (Issue #88, #143, #147 and #161, thanks dalito)
123
124
125 0.5.2 (2014-07-31)
126 ------------------
127
128 - Changed travis config to use miniconda for faster testing.
129 - Added wheel configuration to setup.cfg.
130 - Ensure resource streams are closed after reading.
131 - Require setuptools.
132 (Issue #169)
133 - Implemented real, imag and T Quantity properties.
134 (Issue #171)
135 - Implemented __int__ and __long__ for Quantity
136 (Issue #170)
137 - Fixed SI prefix error on ureg.convert.
138 (Issue #156, thanks jdreaver)
139 - Fixed parsing of multiparemeter contexts.
140 (Issue #174)
141
142
143 0.5.1 (2014-06-03)
144 ------------------
145
146 - Implemented a standard way to change the registry used in unpickling operations.
147 (Issue #148)
148 - Fix bug where conversion would fail due to caching.
149 (Issue #140, thanks jdreaver)
150 - Allow assigning Not a Number to a quantity array.
151 (Issue #127)
152 - Decoupled Quantity in place and not in place unit conversion methods.
153 - Return None in functions that modify quantities in place.
154 - Improved testing infrastructure to check for unwanted warnings.
155 - Added test function at the package level to run all tests.
156
157
158 0.5 (2014-05-07)
159 ----------------
160
161 - Improved test suite helper functions.
162 - Print honors default format w/o format().
163 (Issue #132, thanks mankoff)
164 - Fixed sum() by treating number zero as a special case.
165 (Issue #122, thanks rec)
166 - Improved behaviour in ScaleConverter, OffsetConverter and Quantity.to.
167 (Issue #120)
168 - Reimplemented loading of default definitions to allow Pint in a cx_freeze or similar package.
169 (Issue #118, thanks jbmohler)
170 - Implemented parsing of pretty printed units.
171 (Issue #117, thanks jpgrayson)
172 - Fixed representation of dimensionless quantities.
173 (Issue #112, thanks rec)
174 - Raise error when invalid formatting code is given.
175 (Issue #111, thanks rec)
176 - Default registry to lazy load, raise error on redefinition
177 (Issue #108, thanks rec, aepsil0n)
178 - Added condensed format.
179 (Issue #107, thanks rec)
180 - Added UnitRegistry () operator to parse expression replacing [].
181 (Issue #106, thanks rec)
182 - Optional case insensitive unit parsing.
183 (Issue #105, thanks rec, jeremyfreeman, dbrnz)
184 - Change the Quantity mutability depending on magnitude type.
185 (Issue #104, thanks rec)
186 - Implemented API to list compatible units.
187 (Issue #89)
188 - Implemented cache of key UnitRegistry methods.
189 - Rewrote the Measurement class to use uncertainties.
190 (Issue #24)
191
192
193 0.4.2 (2014-02-14)
194 ------------------
195
196 - Python 2.6 support
197 (Issue #96, thanks tiagocoutinho)
198 - Fixed symbol for inch.
199 (Issue #102, thanks cybertoast)
200 - Stop raising AttributeError when wrapping funcs without all of the attributes.
201 (Issue #100, thanks jturner314)
202 - Fixed warning appearing in Py2.x when comparing a Numpy Array with an empty string.
203 (Issue #98, thanks jturner314)
204 - Add links to AUR packages in docs.
205 (Issue #91, thanks jturner314)
206 - Fixed garbage collection related problem.
207 (Issue #92, thanks jturner314)
208
209
210 0.4.1 (2014-01-12)
211 ------------------
212
213 - Integer Division with Arrays.
214 (Issue #80, thanks jdreaver)
215 - Improved Documentation.
216 (Issue #83, thanks choloepus)
217 - Removed 'h' alias for hour due to conflict with Planck's constant.
218 (Issue #82, thanks choloepus)
219 - Improved get_base_units for non-multiplicative units.
220 (Issue #85, thanks exxus)
221 - Refactored code for multiplication.
222 (Issue #84, thanks jturner314)
223 - Removed 'R' alias for roentgen as it collides with molar_gas_constant.
224 (Issue #87, thanks rsking84)
225 - Improved naming of temperature units and multiplication of non-multiplicative units.
226 (Issue #86, tahsnk exxus)
227
228
229
230 0.4 (2013-12-17)
231 ----------------
232
233 - Introduced Contexts: relation between incompatible dimensions.
234 (Issue #65)
235 - Fixed get_base_units for non multiplicative units.
236 (Related to issue #66)
237 - Implemented default formatting for quantities.
238 - Changed comparison between Quantities containing NumPy arrays.
239 (Issue #75) - BACKWARDS INCOMPATIBLE CHANGE
240 - Fixes for NumPy 1.8 due to changes in handling binary ops.
241 (Issue #73)
242
243
244 0.3.3 (2013-11-29)
245 ------------------
246
247 - ParseHelper can now parse units named like python keywords.
248 (Issue #69)
249 - Fix comparison of quantities.
250 (Issue #74)
251 - Fix Inequality operator.
252 (Issue #70, thanks muggenhor)
253 - Improved travis configuration.
254 (thanks muggenhor)
255
256
257 0.3.2 (2013-10-22)
258 ------------------
259
260 - Fix get_dimensionality for non multiplicative units.
261 (Issue #66)
262 - Proper handling of @import directive inside a file read using pkg_resources.
263 (Issue #68)
264
265
266 0.3.1 (2013-09-15)
267 ------------------
268
269 - fix right division on python 2.7
270 (Issue #58, thanks natezb)
271 - fix formatting of fractional exponentials between 0 and 1.
272 (Issue #62, thanks jdreaver)
273 - fix installation as egg.
274 (Issue #61)
275 - fix handling of strange values as input of Quantity.
276 (Issue #53)
277 - math operations between quantities of different registries now raise a ValueError.
278 (Issue #52)
279
280
281 0.3 (2013-09-02)
282 ----------------
283
284 - support for IPython autocomplete and rich display.
285 (Issues #30 and #31)
286 - support for @import directive in definitions file.
287 (Issue #22)
288 - support for wrapping functions to make them pint-aware.
289 (Issue #16)
290 - support for comparing UnitsContainer to string.
291 (Issue #35)
292 - fix error raised while converting from a single unit to one expressed as
293 the relation between many.
294 (Issue #29)
295 - fix error raised when unit symbol is missing.
296 (Issue #41)
297 - fix error raised when magnitude is Decimal.
298 (Issue #46, thanks danielsokolowski)
299 - support for non-installed pint.
300 (Issue #42, thanks danielsokolowski)
301 - support for application of numpy function on non-ndarray magnitudes.
302 (Issue #44)
303 - support for math operations on dimensionless Quantities (written with units).
304 (Issue #45)
305 - fix obtaining dimensionless quantity from string.
306 (Issue #50)
307 - fix adding and comparing numbers to a dimensionless quantity (written with units).
308 (Issue #54)
309 - Support for iter in Quantity.
310 (Issue #55, thanks natezb)
311
312
313 0.2.1 (2013-07-02)
314 ------------------
315
316 - fix error raised while converting from a single unit to one expressed as
317 the relation between many.
318 (Issue #29)
319
320
321 0.2 (2013-05-13)
322 ----------------
323
324 - support for Measurement (Quantity +/- error).
325 - implemented buckingham pi theorem for dimensional analysis.
326 - support for temperature units and temperature difference units.
327 - parser can infers if the user mean temperature or temperature difference.
328 - support for derived dimensions (e.g. [speed] = [length] / [time]).
329 - refactored the code into multiple files.
330 - refactored code to isolate definitions and converters.
331 - refactored formatter out of UnitParser class.
332 - added tox and travis config files for CI.
333 - comprehensive NumPy testing including almost all ufuncs.
334 - full NumPy support (features is not longer experimental).
335 - fixed bug preventing from having independent registries.
336 (Issue #10, thanks bwanders)
337 - forces real division as default for Quantities.
338 (Issue #7, thanks dbrnz)
339 - improved default unit definition file.
340 (Issue #13, thanks r-barnes)
341 - smarter parser supporting spaces as multiplications and other nice features.
342 (Issue #13, thanks r-barnes)
343 - moved testsuite inside package.
344 - short forms of binary prefixes, more units and fix to less than comparison.
345 (Issue #20, thanks muggenhor)
346 - pint is now zip-safe
347 (Issue #23, thanks muggenhor)
348
349
350 Version 0.1.3 (2013-01-07)
351 --------------------------
352
353 - abbreviated quantity string formating.
354 - complete Python 2.7 compatibility.
355 - implemented pickle support for Quantities objects.
356 - extended NumPy support.
357 - various bugfixes.
358
359
360 Version 0.1.2 (2012-08-12)
361 --------------------------
362
363 - experimenal NumPy support.
364 - included default unit definitions file.
365 (Issue #1, thanks fish2000)
366 - better testing.
367 - various bugfixes.
368 - fixed some units definitions.
369 (Issue #4, thanks craigholm)
370
371
372 Version 0.1.1 (2012-07-31)
373 --------------------------
374
375 - better packaging and installation.
376
377
378 Version 0.1 (2012-07-26)
379 --------------------------
380
381 - first public release.
382
383Keywords: physical quantities unit conversion science
384Platform: UNKNOWN
385Classifier: Development Status :: 4 - Beta
386Classifier: Intended Audience :: Developers
387Classifier: Intended Audience :: Science/Research
388Classifier: License :: OSI Approved :: BSD License
389Classifier: Operating System :: MacOS :: MacOS X
390Classifier: Operating System :: Microsoft :: Windows
391Classifier: Operating System :: POSIX
392Classifier: Programming Language :: Python
393Classifier: Topic :: Scientific/Engineering
394Classifier: Topic :: Software Development :: Libraries
395Classifier: Programming Language :: Python :: 2.6
396Classifier: Programming Language :: Python :: 2.7
397Classifier: Programming Language :: Python :: 3.0
398Classifier: Programming Language :: Python :: 3.1
399Classifier: Programming Language :: Python :: 3.2
400Classifier: Programming Language :: Python :: 3.3
0401
=== added directory 'Pint.egg-info'
=== added file 'Pint.egg-info/PKG-INFO'
--- Pint.egg-info/PKG-INFO 1970-01-01 00:00:00 +0000
+++ Pint.egg-info/PKG-INFO 2015-01-07 15:51:38 +0000
@@ -0,0 +1,400 @@
1Metadata-Version: 1.1
2Name: Pint
3Version: 0.6
4Summary: Physical quantities module
5Home-page: https://github.com/hgrecco/pint
6Author: Hernan E. Grecco
7Author-email: hernan.grecco@gmail.com
8License: BSD
9Description: Pint: a Python units library
10 ============================
11
12 Pint is Python module/package to define, operate and manipulate physical
13 quantities: the product of a numerical value and a unit of measurement.
14 It allows arithmetic operations between them and conversions from and
15 to different units.
16
17 It is distributed with a comprehensive list of physical units, prefixes
18 and constants. Due to its modular design, you to extend (or even rewrite!)
19 the complete list without changing the source code.
20
21 It has a complete test coverage. It runs in Python 2.7 and 3.X
22 with no other dependency. It licensed under BSD.
23
24
25 Design principles
26 -----------------
27
28 Although there are already a few very good Python packages to handle physical
29 quantities, no one was really fitting my needs. Like most developers, I programed
30 Pint to scratch my own itches.
31
32 - Unit parsing: prefixed and pluralized forms of units are recognized without
33 explicitly defining them. In other words: as the prefix *kilo* and the unit *meter*
34 are defined, Pint understands *kilometers*. This results in a much shorter and
35 maintainable unit definition list as compared to other packages.
36
37 - Standalone unit definitions: units definitions are loaded from simple and
38 easy to edit text file. Adding and changing units and their definitions does
39 not involve changing the code.
40
41 - Advanced string formatting: a quantity can be formatted into string using
42 PEP 3101 syntax. Extended conversion flags are given to provide latex and pretty
43 formatting.
44
45 - Small codebase: small and easy to maintain with a flat hierarchy.
46
47 - Dependency free: it depends only on Python and its standard library.
48
49 - Python 2 and 3: A single codebase that runs unchanged in Python 2.7+ and Python 3.0+.
50
51 - Advanced NumPy support: While NumPy is not a requirement for Pint,
52 when available ndarray methods and ufuncs can be used in Quantity objects.
53
54
55 Pint is written and maintained by Hernan E. Grecco <hernan.grecco@gmail.com>.
56
57 Other contributors, listed alphabetically, are:
58
59 * Alexander Böhn <fish2000@gmail.com>
60 * Brend Wanders <b.wanders@utwente.nl>
61 * choloepus
62 * Daniel Sokolowski <daniel.sokolowski@danols.com>
63 * Dave Brooks <dave@bcs.co.nz>
64 * David Linke
65 * Eduard Bopp <eduard.bopp@aepsil0n.de>
66 * Felix Hummel <felix@felixhummel.de>
67 * Giel van Schijndel <me@mortis.eu>
68 * James Rowe <jnrowe@gmail.com>
69 * Jim Turner <jturner314@gmail.com>
70 * Joel B. Mohler <joel@kiwistrawberry.us>
71 * John David Reaver <jdreaver@adlerhorst.com>
72 * Jonas Olson <jolson@kth.se>
73 * Kenneth D. Mankoff <mankoff@gmail.com>
74 * Luke Campbell <luke.s.campbell@gmail.com>
75 * Matthieu Dartiailh <marul@laposte.net>
76 * Nate Bogdanowicz <natezb@gmail.com>
77 * Peter Grayson <jpgrayson@gmail.com>
78 * Richard Barnes <rbarnes@umn.edu>
79 * Ryan Kingsbury <RyanSKingsbury@alumni.unc.edu>
80 * Sundar Raman <cybertoast@gmail.com>
81 * Tiago Coutinho <coutinho@esrf.fr>
82 * Tom Ritchford <tom@swirly.com>
83 * Virgil Dupras <virgil.dupras@savoirfairelinux.com>
84
85 (If you think that your name belongs here, please let the maintainer know)
86
87
88 Pint Changelog
89 ==============
90
91
92 0.6 (2014-11-07)
93 ----------------
94
95 - Fix operations with measurments and user defined units.
96 (Issue #204)
97 - Faster conversions through caching and other performance improvements.
98 (Issue #193, thanks MatthieuDartiailh)
99 - Better error messages on Quantity.__setitem__.
100 (Issue #191)
101 - Fixed abbreviation of fluid_ounce.
102 (Issue #187, thanks hsoft)
103 - Defined Angstrom symbol.
104 (Issue #181, thanks JonasOlson)
105 - Removed fetching version from git repo as it triggers XCode installation on OSX.
106 (Issue #178, thanks deanishe)
107 - Improved context documentation.
108 (Issue #176 and 179, thanks rsking84)
109 - Added Chemistry context.
110 (Issue #179, thanks rsking84)
111 - Fix help(UnitRegisty)
112 (Issue #168)
113 - Optimized "get_dimensionality" and "get_base_name".
114 (Issue #166 and #167, thanks jbmohler)
115 - Renamed ureg.parse_units parameter "to_delta" to "as_delta" to make clear.
116 that no conversion happens. Accordingly, the parameter/property
117 "default_to_delta" of UnitRegistry was renamed to "default_as_delta".
118 (Issue #158, thanks dalit)
119 - Fixed problem when adding two uncertainties.
120 (thanks dalito)
121 - Full support for Offset units (e.g. temperature)
122 (Issue #88, #143, #147 and #161, thanks dalito)
123
124
125 0.5.2 (2014-07-31)
126 ------------------
127
128 - Changed travis config to use miniconda for faster testing.
129 - Added wheel configuration to setup.cfg.
130 - Ensure resource streams are closed after reading.
131 - Require setuptools.
132 (Issue #169)
133 - Implemented real, imag and T Quantity properties.
134 (Issue #171)
135 - Implemented __int__ and __long__ for Quantity
136 (Issue #170)
137 - Fixed SI prefix error on ureg.convert.
138 (Issue #156, thanks jdreaver)
139 - Fixed parsing of multiparemeter contexts.
140 (Issue #174)
141
142
143 0.5.1 (2014-06-03)
144 ------------------
145
146 - Implemented a standard way to change the registry used in unpickling operations.
147 (Issue #148)
148 - Fix bug where conversion would fail due to caching.
149 (Issue #140, thanks jdreaver)
150 - Allow assigning Not a Number to a quantity array.
151 (Issue #127)
152 - Decoupled Quantity in place and not in place unit conversion methods.
153 - Return None in functions that modify quantities in place.
154 - Improved testing infrastructure to check for unwanted warnings.
155 - Added test function at the package level to run all tests.
156
157
158 0.5 (2014-05-07)
159 ----------------
160
161 - Improved test suite helper functions.
162 - Print honors default format w/o format().
163 (Issue #132, thanks mankoff)
164 - Fixed sum() by treating number zero as a special case.
165 (Issue #122, thanks rec)
166 - Improved behaviour in ScaleConverter, OffsetConverter and Quantity.to.
167 (Issue #120)
168 - Reimplemented loading of default definitions to allow Pint in a cx_freeze or similar package.
169 (Issue #118, thanks jbmohler)
170 - Implemented parsing of pretty printed units.
171 (Issue #117, thanks jpgrayson)
172 - Fixed representation of dimensionless quantities.
173 (Issue #112, thanks rec)
174 - Raise error when invalid formatting code is given.
175 (Issue #111, thanks rec)
176 - Default registry to lazy load, raise error on redefinition
177 (Issue #108, thanks rec, aepsil0n)
178 - Added condensed format.
179 (Issue #107, thanks rec)
180 - Added UnitRegistry () operator to parse expression replacing [].
181 (Issue #106, thanks rec)
182 - Optional case insensitive unit parsing.
183 (Issue #105, thanks rec, jeremyfreeman, dbrnz)
184 - Change the Quantity mutability depending on magnitude type.
185 (Issue #104, thanks rec)
186 - Implemented API to list compatible units.
187 (Issue #89)
188 - Implemented cache of key UnitRegistry methods.
189 - Rewrote the Measurement class to use uncertainties.
190 (Issue #24)
191
192
193 0.4.2 (2014-02-14)
194 ------------------
195
196 - Python 2.6 support
197 (Issue #96, thanks tiagocoutinho)
198 - Fixed symbol for inch.
199 (Issue #102, thanks cybertoast)
200 - Stop raising AttributeError when wrapping funcs without all of the attributes.
201 (Issue #100, thanks jturner314)
202 - Fixed warning appearing in Py2.x when comparing a Numpy Array with an empty string.
203 (Issue #98, thanks jturner314)
204 - Add links to AUR packages in docs.
205 (Issue #91, thanks jturner314)
206 - Fixed garbage collection related problem.
207 (Issue #92, thanks jturner314)
208
209
210 0.4.1 (2014-01-12)
211 ------------------
212
213 - Integer Division with Arrays.
214 (Issue #80, thanks jdreaver)
215 - Improved Documentation.
216 (Issue #83, thanks choloepus)
217 - Removed 'h' alias for hour due to conflict with Planck's constant.
218 (Issue #82, thanks choloepus)
219 - Improved get_base_units for non-multiplicative units.
220 (Issue #85, thanks exxus)
221 - Refactored code for multiplication.
222 (Issue #84, thanks jturner314)
223 - Removed 'R' alias for roentgen as it collides with molar_gas_constant.
224 (Issue #87, thanks rsking84)
225 - Improved naming of temperature units and multiplication of non-multiplicative units.
226 (Issue #86, tahsnk exxus)
227
228
229
230 0.4 (2013-12-17)
231 ----------------
232
233 - Introduced Contexts: relation between incompatible dimensions.
234 (Issue #65)
235 - Fixed get_base_units for non multiplicative units.
236 (Related to issue #66)
237 - Implemented default formatting for quantities.
238 - Changed comparison between Quantities containing NumPy arrays.
239 (Issue #75) - BACKWARDS INCOMPATIBLE CHANGE
240 - Fixes for NumPy 1.8 due to changes in handling binary ops.
241 (Issue #73)
242
243
244 0.3.3 (2013-11-29)
245 ------------------
246
247 - ParseHelper can now parse units named like python keywords.
248 (Issue #69)
249 - Fix comparison of quantities.
250 (Issue #74)
251 - Fix Inequality operator.
252 (Issue #70, thanks muggenhor)
253 - Improved travis configuration.
254 (thanks muggenhor)
255
256
257 0.3.2 (2013-10-22)
258 ------------------
259
260 - Fix get_dimensionality for non multiplicative units.
261 (Issue #66)
262 - Proper handling of @import directive inside a file read using pkg_resources.
263 (Issue #68)
264
265
266 0.3.1 (2013-09-15)
267 ------------------
268
269 - fix right division on python 2.7
270 (Issue #58, thanks natezb)
271 - fix formatting of fractional exponentials between 0 and 1.
272 (Issue #62, thanks jdreaver)
273 - fix installation as egg.
274 (Issue #61)
275 - fix handling of strange values as input of Quantity.
276 (Issue #53)
277 - math operations between quantities of different registries now raise a ValueError.
278 (Issue #52)
279
280
281 0.3 (2013-09-02)
282 ----------------
283
284 - support for IPython autocomplete and rich display.
285 (Issues #30 and #31)
286 - support for @import directive in definitions file.
287 (Issue #22)
288 - support for wrapping functions to make them pint-aware.
289 (Issue #16)
290 - support for comparing UnitsContainer to string.
291 (Issue #35)
292 - fix error raised while converting from a single unit to one expressed as
293 the relation between many.
294 (Issue #29)
295 - fix error raised when unit symbol is missing.
296 (Issue #41)
297 - fix error raised when magnitude is Decimal.
298 (Issue #46, thanks danielsokolowski)
299 - support for non-installed pint.
300 (Issue #42, thanks danielsokolowski)
301 - support for application of numpy function on non-ndarray magnitudes.
302 (Issue #44)
303 - support for math operations on dimensionless Quantities (written with units).
304 (Issue #45)
305 - fix obtaining dimensionless quantity from string.
306 (Issue #50)
307 - fix adding and comparing numbers to a dimensionless quantity (written with units).
308 (Issue #54)
309 - Support for iter in Quantity.
310 (Issue #55, thanks natezb)
311
312
313 0.2.1 (2013-07-02)
314 ------------------
315
316 - fix error raised while converting from a single unit to one expressed as
317 the relation between many.
318 (Issue #29)
319
320
321 0.2 (2013-05-13)
322 ----------------
323
324 - support for Measurement (Quantity +/- error).
325 - implemented buckingham pi theorem for dimensional analysis.
326 - support for temperature units and temperature difference units.
327 - parser can infers if the user mean temperature or temperature difference.
328 - support for derived dimensions (e.g. [speed] = [length] / [time]).
329 - refactored the code into multiple files.
330 - refactored code to isolate definitions and converters.
331 - refactored formatter out of UnitParser class.
332 - added tox and travis config files for CI.
333 - comprehensive NumPy testing including almost all ufuncs.
334 - full NumPy support (features is not longer experimental).
335 - fixed bug preventing from having independent registries.
336 (Issue #10, thanks bwanders)
337 - forces real division as default for Quantities.
338 (Issue #7, thanks dbrnz)
339 - improved default unit definition file.
340 (Issue #13, thanks r-barnes)
341 - smarter parser supporting spaces as multiplications and other nice features.
342 (Issue #13, thanks r-barnes)
343 - moved testsuite inside package.
344 - short forms of binary prefixes, more units and fix to less than comparison.
345 (Issue #20, thanks muggenhor)
346 - pint is now zip-safe
347 (Issue #23, thanks muggenhor)
348
349
350 Version 0.1.3 (2013-01-07)
351 --------------------------
352
353 - abbreviated quantity string formating.
354 - complete Python 2.7 compatibility.
355 - implemented pickle support for Quantities objects.
356 - extended NumPy support.
357 - various bugfixes.
358
359
360 Version 0.1.2 (2012-08-12)
361 --------------------------
362
363 - experimenal NumPy support.
364 - included default unit definitions file.
365 (Issue #1, thanks fish2000)
366 - better testing.
367 - various bugfixes.
368 - fixed some units definitions.
369 (Issue #4, thanks craigholm)
370
371
372 Version 0.1.1 (2012-07-31)
373 --------------------------
374
375 - better packaging and installation.
376
377
378 Version 0.1 (2012-07-26)
379 --------------------------
380
381 - first public release.
382
383Keywords: physical quantities unit conversion science
384Platform: UNKNOWN
385Classifier: Development Status :: 4 - Beta
386Classifier: Intended Audience :: Developers
387Classifier: Intended Audience :: Science/Research
388Classifier: License :: OSI Approved :: BSD License
389Classifier: Operating System :: MacOS :: MacOS X
390Classifier: Operating System :: Microsoft :: Windows
391Classifier: Operating System :: POSIX
392Classifier: Programming Language :: Python
393Classifier: Topic :: Scientific/Engineering
394Classifier: Topic :: Software Development :: Libraries
395Classifier: Programming Language :: Python :: 2.6
396Classifier: Programming Language :: Python :: 2.7
397Classifier: Programming Language :: Python :: 3.0
398Classifier: Programming Language :: Python :: 3.1
399Classifier: Programming Language :: Python :: 3.2
400Classifier: Programming Language :: Python :: 3.3
0401
=== added file 'Pint.egg-info/SOURCES.txt'
--- Pint.egg-info/SOURCES.txt 1970-01-01 00:00:00 +0000
+++ Pint.egg-info/SOURCES.txt 2015-01-07 15:51:38 +0000
@@ -0,0 +1,72 @@
1AUTHORS
2CHANGES
3CHANGES_DEV
4LICENSE
5MANIFEST.in
6README
7setup.cfg
8setup.py
9Pint.egg-info/PKG-INFO
10Pint.egg-info/SOURCES.txt
11Pint.egg-info/dependency_links.txt
12Pint.egg-info/entry_points.txt
13Pint.egg-info/top_level.txt
14Pint.egg-info/zip-safe
15bench/bench.py
16bench/bench_base.yaml
17bench/bench_numpy.yaml
18docs/Makefile
19docs/conf.py
20docs/contexts.rst
21docs/contributing.rst
22docs/defining.rst
23docs/faq.rst
24docs/getting.rst
25docs/index.rst
26docs/make.bat
27docs/measurement.rst
28docs/nonmult.rst
29docs/numpy.rst
30docs/pitheorem.rst
31docs/serialization.rst
32docs/tutorial.rst
33docs/wrapping.rst
34docs/_static/logo-full.jpg
35docs/_templates/sidebarintro.html
36docs/_templates/sidebarlogo.html
37docs/_themes/.gitignore
38docs/_themes/LICENSE
39docs/_themes/README
40docs/_themes/flask_theme_support.py
41docs/_themes/flask/layout.html
42docs/_themes/flask/relations.html
43docs/_themes/flask/theme.conf
44docs/_themes/flask/static/flasky.css_t
45docs/_themes/flask/static/small_flask.css
46pint/__init__.py
47pint/constants_en.txt
48pint/context.py
49pint/default_en.txt
50pint/formatting.py
51pint/measurement.py
52pint/quantity.py
53pint/unit.py
54pint/util.py
55pint/compat/__init__.py
56pint/compat/chainmap.py
57pint/compat/lrucache.py
58pint/compat/nullhandler.py
59pint/compat/transformdict.py
60pint/testsuite/__init__.py
61pint/testsuite/helpers.py
62pint/testsuite/parameterized.py
63pint/testsuite/test_contexts.py
64pint/testsuite/test_formatter.py
65pint/testsuite/test_issues.py
66pint/testsuite/test_measurement.py
67pint/testsuite/test_numpy.py
68pint/testsuite/test_pitheorem.py
69pint/testsuite/test_quantity.py
70pint/testsuite/test_umath.py
71pint/testsuite/test_unit.py
72pint/testsuite/test_util.py
0\ No newline at end of file73\ No newline at end of file
174
=== added file 'Pint.egg-info/dependency_links.txt'
--- Pint.egg-info/dependency_links.txt 1970-01-01 00:00:00 +0000
+++ Pint.egg-info/dependency_links.txt 2015-01-07 15:51:38 +0000
@@ -0,0 +1,1 @@
1
02
=== added file 'Pint.egg-info/entry_points.txt'
--- Pint.egg-info/entry_points.txt 1970-01-01 00:00:00 +0000
+++ Pint.egg-info/entry_points.txt 2015-01-07 15:51:38 +0000
@@ -0,0 +1,3 @@
1[zest.releaser.releaser.after_checkout]
2pyroma = pint:_run_pyroma
3
04
=== added file 'Pint.egg-info/top_level.txt'
--- Pint.egg-info/top_level.txt 1970-01-01 00:00:00 +0000
+++ Pint.egg-info/top_level.txt 2015-01-07 15:51:38 +0000
@@ -0,0 +1,1 @@
1pint
02
=== added file 'Pint.egg-info/zip-safe'
--- Pint.egg-info/zip-safe 1970-01-01 00:00:00 +0000
+++ Pint.egg-info/zip-safe 2015-01-07 15:51:38 +0000
@@ -0,0 +1,1 @@
1
02
=== modified file 'debian/changelog'
--- debian/changelog 2014-09-05 23:26:05 +0000
+++ debian/changelog 2015-01-07 15:51:38 +0000
@@ -1,3 +1,10 @@
1python-pint (0.6-0ubuntu1) UNRELEASED; urgency=medium
2
3 * New upstream release.
4 * d/control: Add python-numpy and python3-numpy to Build-Depends.
5
6 -- Corey Bryant <corey.bryant@canonical.com> Wed, 07 Jan 2015 10:47:04 -0500
7
1python-pint (0.5.2-1) unstable; urgency=medium8python-pint (0.5.2-1) unstable; urgency=medium
29
3 * Initial release. (Closes: #760586)10 * Initial release. (Closes: #760586)
411
=== modified file 'debian/control'
--- debian/control 2014-09-05 23:26:05 +0000
+++ debian/control 2015-01-07 15:51:38 +0000
@@ -6,11 +6,13 @@
6Build-Depends: debhelper (>= 9),6Build-Depends: debhelper (>= 9),
7 python-all (>= 2.6.6-3~),7 python-all (>= 2.6.6-3~),
8 python-nose,8 python-nose,
9 python-numpy,
9 python-setuptools,10 python-setuptools,
10 python-sphinx,11 python-sphinx,
11 python-yaml,12 python-yaml,
12 python3-all,13 python3-all,
13 python3-nose,14 python3-nose,
15 python3-numpy,
14 python3-setuptools,16 python3-setuptools,
15 python3-yaml17 python3-yaml
16Standards-Version: 3.9.518Standards-Version: 3.9.5
1719
=== modified file 'debian/watch'
--- debian/watch 2014-09-05 23:26:05 +0000
+++ debian/watch 2015-01-07 15:51:38 +0000
@@ -1,3 +1,3 @@
1version=31version=3
2http://pypi.python.org/packages/source/P/Pint Pint-(.*).tar.gz2opts="uversionmangle=s/.zip//" \
33 http://pypi.python.org/packages/source/P/Pint/ Pint-(.*).zip
44
=== modified file 'docs/_themes/flask/layout.html'
--- docs/_themes/flask/layout.html 2014-09-05 23:26:05 +0000
+++ docs/_themes/flask/layout.html 2015-01-07 15:51:38 +0000
@@ -10,7 +10,7 @@
10{%- block relbar2 %}10{%- block relbar2 %}
11 {% if theme_github_fork %}11 {% if theme_github_fork %}
12 <a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"12 <a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
13 src="" alt="Fork me on GitHub" /></a>13 src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
14 {% endif %}14 {% endif %}
15{% endblock %}15{% endblock %}
16{% block header %}16{% block header %}
1717
=== modified file 'docs/conf.py'
--- docs/conf.py 2014-09-05 23:26:05 +0000
+++ docs/conf.py 2015-01-07 15:51:38 +0000
@@ -28,7 +28,7 @@
2828
29# Add any Sphinx extension module names here, as strings. They can be extensions29# Add any Sphinx extension module names here, as strings. They can be extensions
30# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.30# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
31extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']31extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax']
3232
33# Add any paths that contain templates here, relative to this directory.33# Add any paths that contain templates here, relative to this directory.
34templates_path = ['_templates']34templates_path = ['_templates']
3535
=== modified file 'docs/contexts.rst'
--- docs/contexts.rst 2014-09-05 23:26:05 +0000
+++ docs/contexts.rst 2015-01-07 15:51:38 +0000
@@ -108,6 +108,15 @@
108 >>> f.to('nm', 'sp', n=1.33)108 >>> f.to('nm', 'sp', n=1.33)
109 <Quantity(398.496240602, 'nanometer')>109 <Quantity(398.496240602, 'nanometer')>
110110
111Contexts can also accept Pint Quantity objects as parameters. For example, the 'chemistry'
112context accepts the molecular weight of a substance (as a Quantity with dimensions of
113[mass]/[substance]) to allow conversion between moles and mass.
114
115.. doctest::
116
117 >>> substance = 95 * ureg('g')
118 >>> moles = substance.to('moles', 'chemistry', mw = 5 * ureg('g/mol'))
119 <Quantity(19.0, 'mole')>
111120
112121
113Defining contexts in a file122Defining contexts in a file
@@ -129,7 +138,8 @@
129All parameters are named and default values are mandatory. Multiple parameters138All parameters are named and default values are mandatory. Multiple parameters
130are separated by commas (like in a python function definition). Finally, you provide the name139are separated by commas (like in a python function definition). Finally, you provide the name
131of the context (e.g. spectroscopy) and, optionally, a short version of the name (e.g. sp)140of the context (e.g. spectroscopy) and, optionally, a short version of the name (e.g. sp)
132separated by an equal sign.141separated by an equal sign. See the definition of the 'chemistry' context in default_en.txt
142for an example of a multiple-parameter context.
133143
134Conversions rules are specified by providing source and destination dimensions separated144Conversions rules are specified by providing source and destination dimensions separated
135using a colon (`:`) from the equation. A special variable named `value` will be replaced145using a colon (`:`) from the equation. A special variable named `value` will be replaced
@@ -140,6 +150,8 @@
140from the first dimension to the second one. A double arrow (`<->`) is used to150from the first dimension to the second one. A double arrow (`<->`) is used to
141indicate that the transformation operates both ways.151indicate that the transformation operates both ways.
142152
153Context definitions are stored and imported exactly like custom units definition file
154(and can be included in the same file as unit definitions). See "Defining units" for details.
143155
144Defining contexts programmatically156Defining contexts programmatically
145----------------------------------157----------------------------------
146158
=== modified file 'docs/faq.rst'
--- docs/faq.rst 2014-09-05 23:26:05 +0000
+++ docs/faq.rst 2015-01-07 15:51:38 +0000
@@ -13,6 +13,8 @@
13You mention other similar Python libraries. Can you point me to those?13You mention other similar Python libraries. Can you point me to those?
14----------------------------------------------------------------------14----------------------------------------------------------------------
1515
16`natu <http://kdavies4.github.io/natu/>`_
17
16`Buckingham <https://code.google.com/p/buckingham/>`_18`Buckingham <https://code.google.com/p/buckingham/>`_
1719
18`Magnitude <http://github.com/juanre/magnitude.git>`_20`Magnitude <http://github.com/juanre/magnitude.git>`_
1921
=== modified file 'docs/index.rst'
--- docs/index.rst 2014-09-05 23:26:05 +0000
+++ docs/index.rst 2015-01-07 15:51:38 +0000
@@ -9,7 +9,7 @@
99
10Pint 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.10Pint 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.
1111
12It 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.12It 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.
1313
14It has a complete test coverage. It runs in Python 2.6+ and 3.2+ with no other dependency. It licensed under BSD.14It has a complete test coverage. It runs in Python 2.6+ and 3.2+ with no other dependency. It licensed under BSD.
1515
1616
=== modified file 'docs/nonmult.rst'
--- docs/nonmult.rst 2014-09-05 23:26:05 +0000
+++ docs/nonmult.rst 2015-01-07 15:51:38 +0000
@@ -4,17 +4,17 @@
4Temperature conversion4Temperature conversion
5======================5======================
66
7Unlike meters and seconds, fahrenheits, celsius and kelvin are not7Unlike meters and seconds, the temperature units fahrenheits and
8multiplicative units. Temperature is expressed in a system with a8celsius are non-multiplicative units. These temperature units are
9reference point, and relations between temperature units include9expressed in a system with a reference point, and relations between
10not only an scaling factor but also an offset. Pint supports these10temperature units include not only a scaling factor but also an offset.
11type of units and conversions between them. The default definition11Pint supports these type of units and conversions between them.
12file includes fahrenheits, celsius, kelvin and rankine abbreviated12The default definition file includes fahrenheits, celsius,
13as degF, degC, degK, and degR.13kelvin and rankine abbreviated as degF, degC, degK, and degR.
1414
15For example, to convert from celsius to fahrenheit:15For example, to convert from celsius to fahrenheit:
1616
17.. testsetup:: *17.. testsetup::
1818
19 from pint import UnitRegistry19 from pint import UnitRegistry
20 ureg = UnitRegistry()20 ureg = UnitRegistry()
@@ -24,21 +24,23 @@
2424
25 >>> from pint import UnitRegistry25 >>> from pint import UnitRegistry
26 >>> ureg = UnitRegistry()26 >>> ureg = UnitRegistry()
27 >>> home = 25.4 * ureg.degC27 >>> Q_ = ureg.Quantity
28 >>> home = Q_(25.4, ureg.degC)
28 >>> print(home.to('degF'))29 >>> print(home.to('degF'))
29 77.72000039999993 degF30 77.7200004 degF
3031
31or to other kelvin or rankine:32or to other kelvin or rankine:
3233
33.. doctest::34.. doctest::
3435
35 >>> print(home.to('degK'))36 >>> print(home.to('kelvin'))
36 298.54999999999995 degK37 298.55 kelvin
37 >>> print(home.to('degR'))38 >>> print(home.to('degR'))
38 537.39 degR39 537.39 degR
3940
40Additionally, for every temperature unit in the registry,41Additionally, for every non-multiplicative temperature unit
41there is also a *delta* counterpart to specify differences.42in the registry, there is also a *delta* counterpart to specify
43differences. Absolute units have no *delta* counterpart.
42For example, the change in celsius is equal to the change44For example, the change in celsius is equal to the change
43in kelvin, but not in fahrenheit (as the scaling factor45in kelvin, but not in fahrenheit (as the scaling factor
44is different).46is different).
@@ -46,20 +48,61 @@
46.. doctest::48.. doctest::
4749
48 >>> increase = 12.3 * ureg.delta_degC50 >>> increase = 12.3 * ureg.delta_degC
49 >>> print(increase.to(ureg.delta_degK))51 >>> print(increase.to(ureg.kelvin))
50 12.3 delta_degK52 12.3 kelvin
51 >>> print(increase.to(ureg.delta_degF))53 >>> print(increase.to(ureg.delta_degF))
52 6.833333333333334 delta_degF54 22.14 delta_degF
5355
54..56..
55 Subtraction of two temperatures also yields a *delta* unit.57Subtraction of two temperatures given in offset units yields a *delta* unit:
5658
57 .. doctest::59.. doctest::
5860
59 >>> 25.4 * ureg.degC - 10. * ureg.degC61 >>> Q_(25.4, ureg.degC) - Q_(10., ureg.degC)
60 15.4 delta_degC62 <Quantity(15.4, 'delta_degC')>
6163
62Differences in temperature are multiplicative:64You can add or subtract a quantity with *delta* unit and a quantity with
65offset unit:
66
67.. doctest::
68
69 >>> Q_(25.4, ureg.degC) + Q_(10., ureg.delta_degC)
70 <Quantity(35.4, 'degC')>
71 >>> Q_(25.4, ureg.degC) - Q_(10., ureg.delta_degC)
72 <Quantity(15.4, 'degC')>
73
74If you want to add a quantity with absolute unit to one with offset unit, like here
75
76.. doctest::
77
78 >>> heating_rate = 0.5 * ureg.kelvin/ureg.min
79 >>> Q_(10., ureg.degC) + heating_rate * Q_(30, ureg.min)
80 Traceback (most recent call last):
81 ...
82 pint.unit.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC, kelvin).
83
84you have to avoid the ambiguity by either converting the offset unit to the
85absolute unit before addition
86
87.. doctest::
88
89 >>> Q_(10., ureg.degC).to(ureg.kelvin) + heating_rate * Q_(30, ureg.min)
90 <Quantity(298.15, 'kelvin')>
91
92or convert the absolute unit to a *delta* unit:
93
94.. doctest::
95
96 >>> Q_(10., ureg.degC) + heating_rate.to('delta_degC/min') * Q_(30, ureg.min)
97 <Quantity(25.0, 'degC')>
98
99In contrast to subtraction, the addition of quantities with offset units
100is ambiguous, e.g. for *10 degC + 100 degC* two different result are reasonable
101depending on the context, *110 degC* or *383.15 °C (= 283.15 K + 373.15 K)*.
102Because of this ambiguity pint raises an error for the addition of two
103quantities with offset units (since pint-0.6).
104
105Quantities with *delta* units are multiplicative:
63106
64.. doctest::107.. doctest::
65108
@@ -67,7 +110,55 @@
67 >>> print(speed.to('delta_degC/second'))110 >>> print(speed.to('delta_degC/second'))
68 1.0 delta_degC / second111 1.0 delta_degC / second
69112
70The parser knows about *delta* units and use them when a temperature unit113However, multiplication, division and exponentiation of quantities with
114offset units is problematic just like addition. Pint (since version 0.6)
115will by default raise an error when a quantity with offset unit is used in
116these operations. Due to this quantities with offset units cannot be created
117like other quantities by multiplication of magnitude and unit but have
118to be explicitly created:
119
120.. doctest::
121
122 >>> home = 25.4 * ureg.degC
123 Traceback (most recent call last):
124 ...
125 pint.unit.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).
126 >>> Q_(25.4, ureg.degC)
127 <Quantity(25.4, 'degC')>
128
129As an alternative to raising an error, pint can be configured to work more
130relaxed via setting the UnitRegistry parameter *autoconvert_offset_to_baseunit*
131to true. In this mode, pint behaves differently:
132
133* Multiplication of a quantity with a single offset unit with order +1 by
134 a number or ndarray yields the quantity in the given unit.
135
136.. doctest::
137
138 >>> ureg = UnitRegistry(autoconvert_offset_to_baseunit = True)
139 >>> T = 25.4 * ureg.degC
140 >>> T
141 <Quantity(25.4, 'degC')>
142
143* Before all other multiplications, all divisions and in case of
144 exponentiation [#f1]_ involving quantities with offset-units, pint
145 will convert the quantities with offset units automatically to the
146 corresponding base unit before performing the operation.
147
148 >>> 1/T
149 <Quantity(0.00334952269302, '1 / kelvin')>
150 >>> T * 10 * ureg.meter
151 <Quantity(527.15, 'kelvin * meter')>
152
153You can change the behaviour at any time:
154
155 >>> ureg.autoconvert_offset_to_baseunit = False
156 >>> 1/T
157 Traceback (most recent call last):
158 ...
159 pint.unit.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).
160
161The parser knows about *delta* units and uses them when a temperature unit
71is found in a multiplicative context. For example, here:162is found in a multiplicative context. For example, here:
72163
73.. doctest::164.. doctest::
@@ -86,9 +177,15 @@
86177
87.. doctest::178.. doctest::
88179
89 >>> print(ureg.parse_units('degC/meter', to_delta=False))180 >>> print(ureg.parse_units('degC/meter', as_delta=False))
90 degC / meter181 degC / meter
91182
183Note that the magnitude is left unchanged:
184
185.. doctest::
186
187 >>> Q_(10, 'degC/meter')
188 <Quantity(10, 'delta_degC / meter')>
92189
93To define a new temperature, you need to specify the offset. For example,190To define a new temperature, you need to specify the offset. For example,
94this is the definition of the celsius and fahrenheit::191this is the definition of the celsius and fahrenheit::
@@ -98,3 +195,5 @@
98195
99You do not need to define *delta* units, as they are defined automatically.196You do not need to define *delta* units, as they are defined automatically.
100197
198.. [#f1] If the exponent is +1, the quantity will not be converted to base
199 unit but remains unchanged.
101\ No newline at end of file200\ No newline at end of file
102201
=== modified file 'docs/serialization.rst'
--- docs/serialization.rst 2014-09-05 23:26:05 +0000
+++ docs/serialization.rst 2015-01-07 15:51:38 +0000
@@ -19,7 +19,7 @@
19 >>> import pint19 >>> import pint
20 >>> ureg = pint.UnitRegistry()20 >>> ureg = pint.UnitRegistry()
21 >>> duration = 24.2 * ureg.years21 >>> duration = 24.2 * ureg.years
22 >>> print(duration)22 >>> duration
23 <Quantity(24.2, 'year')>23 <Quantity(24.2, 'year')>
24 >>> serialized = str(duration)24 >>> serialized = str(duration)
25 >>> print(serialized)25 >>> print(serialized)
@@ -37,7 +37,7 @@
37 >>> ureg = pint.UnitRegistry()37 >>> ureg = pint.UnitRegistry()
38 >>> duration = ureg('24.2 year')38 >>> duration = ureg('24.2 year')
39 >>> print(duration)39 >>> print(duration)
40 <Quantity(24.2, 'year')>40 24.2 year
4141
42Notice that the serialized quantity is likely to be parsed in **another** registry42Notice that the serialized quantity is likely to be parsed in **another** registry
43as shown in this example. Pint Quantities do not exist on their own but they are43as shown in this example. Pint Quantities do not exist on their own but they are
@@ -72,6 +72,7 @@
7272
73 >>> magnitude, units = pickle.loads(serialized)73 >>> magnitude, units = pickle.loads(serialized)
74 >>> ureg.Quantity(magnitude, units)74 >>> ureg.Quantity(magnitude, units)
75 <Quantity(24.2, 'year')>
7576
76You can use the same mechanism with any serialization protocol, not only with binary ones.77You can use the same mechanism with any serialization protocol, not only with binary ones.
77(In fact, version 0 of the Pickle protocol is ascii). Other common serialization protocols/packages78(In fact, version 0 of the Pickle protocol is ascii). Other common serialization protocols/packages
7879
=== modified file 'docs/tutorial.rst'
--- docs/tutorial.rst 2014-09-05 23:26:05 +0000
+++ docs/tutorial.rst 2015-01-07 15:51:38 +0000
@@ -96,7 +96,6 @@
96 >>> print(height)96 >>> print(height)
97 5.75 foot97 5.75 foot
98 >>> height.ito_base_units()98 >>> height.ito_base_units()
99 <Quantity(1.7526, 'meter')>
100 >>> print(height)99 >>> print(height)
101 1.7526 meter100 1.7526 meter
102101
103102
=== modified file 'pint/__init__.py'
--- pint/__init__.py 2014-09-05 23:26:05 +0000
+++ pint/__init__.py 2015-01-07 15:51:38 +0000
@@ -16,25 +16,19 @@
16import subprocess16import subprocess
17import pkg_resources17import pkg_resources
18from .formatting import formatter18from .formatting import formatter
19from .unit import UnitRegistry, DimensionalityError, UndefinedUnitError, LazyRegistry19from .unit import (UnitRegistry, DimensionalityError, OffsetUnitCalculusError,
20 UndefinedUnitError, LazyRegistry)
20from .util import pi_theorem, logger21from .util import pi_theorem, logger
2122
22from .context import Context23from .context import Context
2324
2425
25__version__ = "unknown"26try: # pragma: no cover
26try: # pragma: no cover27 __version__ = pkg_resources.get_distribution('pint').version
27 # try to grab the commit version of our package28except: # pragma: no cover
28 __version__ = (subprocess.check_output(["git", "describe"],29 # we seem to have a local copy not installed without setuptools
29 stderr=subprocess.STDOUT,30 # so the reported version will be unknown
30 cwd=os.path.dirname(os.path.abspath(__file__)))).strip()31 __version__ = "unknown"
31except: # pragma: no cover
32 # on any error just try to grab the version that is installed on the system
33 try:
34 __version__ = pkg_resources.get_distribution('pint').version
35 except: # pragma: no cover
36 pass # we seem to have a local copy without any repository control or installed without setuptools
37 # so the reported version will be __unknown__
3832
3933
40#: A Registry with the default units and constants.34#: A Registry with the default units and constants.
@@ -81,6 +75,47 @@
81 sys.exit(1)75 sys.exit(1)
8276
8377
78def _check_travis(data): # pragma: no cover
79 """Check if Travis reports that everything is ok.
80 (used to perform checks before releasing a new version).
81 """
82 import json
83 import sys
84
85 from zest.releaser.utils import system, ask
86 if not ask('Check with Travis before releasing?'):
87 return
88
89 try:
90 # Python 3
91 from urllib.request import urlopen
92 def get(url):
93 return urlopen(url).read().decode('utf-8')
94
95 except ImportError:
96 # Python 2
97 from urllib2 import urlopen
98 def get(url):
99 return urlopen(url).read()
100
101 url = 'https://api.github.com/repos/%s/%s/status/%s'
102
103 username = 'hgrecco'
104 repo = 'pint'
105 commit = system('git rev-parse HEAD')
106
107 try:
108 result = json.loads(get(url % (username, repo, commit)))['state']
109 print('Travis says: %s' % result)
110 if result != 'success':
111 if not ask('Do you want to continue anyway?', default=False):
112 sys.exit(1)
113 except Exception:
114 print('Could not determine the commit state with Travis.')
115 if ask('Do you want to continue anyway?', default=False):
116 sys.exit(1)
117
118
84def test():119def test():
85 """Run all tests.120 """Run all tests.
86121
87122
=== modified file 'pint/default_en.txt'
--- pint/default_en.txt 2014-09-05 23:26:05 +0000
+++ pint/default_en.txt 2015-01-07 15:51:38 +0000
@@ -10,7 +10,7 @@
10femto- = 1e-15 = f-10femto- = 1e-15 = f-
11pico- = 1e-12 = p-11pico- = 1e-12 = p-
12nano- = 1e-9 = n-12nano- = 1e-9 = n-
13micro- = 1e-6 = u-13micro- = 1e-6 = u- = µ-
14milli- = 1e-3 = m-14milli- = 1e-3 = m-
15centi- = 1e-2 = c-15centi- = 1e-2 = c-
16deci- = 1e-1 = d-16deci- = 1e-1 = d-
@@ -76,7 +76,7 @@
76coulomb = ampere * second = C76coulomb = ampere * second = C
77volt = joule / coulomb = V77volt = joule / coulomb = V
78farad = coulomb / volt = F78farad = coulomb / volt = F
79ohm = volt / ampere79ohm = volt / ampere = Ω
80siemens = ampere / volt = S = mho80siemens = ampere / volt = S = mho
81weber = volt * second = Wb81weber = volt * second = Wb
82tesla = weber / meter ** 2 = T82tesla = weber / meter ** 2 = T
@@ -138,7 +138,7 @@
138baud = bit / second = Bd = bps138baud = bit / second = Bd = bps
139139
140# Length140# Length
141angstrom = 1e-10 * meter141angstrom = 1e-10 * meter = ångström = Å
142inch = 2.54 * centimeter = in = international_inch = inches = international_inches 142inch = 2.54 * centimeter = in = international_inch = inches = international_inches
143foot = 12 * inch = ft = international_foot = feet = international_feet143foot = 12 * inch = ft = international_foot = feet = international_feet
144mile = 5280 * foot = mi = international_mile144mile = 5280 * foot = mi = international_mile
@@ -291,7 +291,7 @@
291pint = quart / 2 = pt = liquid_pint = US_liquid_pint291pint = quart / 2 = pt = liquid_pint = US_liquid_pint
292cup = pint / 2 = liquid_cup = US_liquid_cup292cup = pint / 2 = liquid_cup = US_liquid_cup
293gill = cup / 2 = liquid_gill = US_liquid_gill293gill = cup / 2 = liquid_gill = US_liquid_gill
294floz = gill / 4 = fluid_ounce = US_fluid_ounce = US_liquid_ounce294fluid_ounce = gill / 4 = floz = US_fluid_ounce = US_liquid_ounce
295imperial_bushel = 36.36872 * liter = UK_bushel295imperial_bushel = 36.36872 * liter = UK_bushel
296imperial_gallon = imperial_bushel / 8 = UK_gallon296imperial_gallon = imperial_bushel / 8 = UK_gallon
297imperial_quart = imperial_gallon / 4 = UK_quart297imperial_quart = imperial_gallon / 4 = UK_quart
@@ -318,3 +318,33 @@
318 [temperature] -> [energy]: boltzmann_constant * value318 [temperature] -> [energy]: boltzmann_constant * value
319 [energy] -> [temperature]: value / boltzmann_constant319 [energy] -> [temperature]: value / boltzmann_constant
320@end320@end
321
322@context(mw=0,volume=0,solvent_mass=0) chemistry = chem
323 # mw is the molecular weight of the species
324 # volume is the volume of the solution
325 # solvent_mass is the mass of solvent in the solution
326
327 # moles -> mass require the molecular weight
328 [substance] -> [mass]: value * mw
329 [mass] -> [substance]: value / mw
330
331 # moles/volume -> mass/volume and moles/mass -> mass / mass
332 # require the molecular weight
333 [substance] / [volume] -> [mass] / [volume]: value * mw
334 [mass] / [volume] -> [substance] / [volume]: value / mw
335 [substance] / [mass] -> [mass] / [mass]: value * mw
336 [mass] / [mass] -> [substance] / [mass]: value / mw
337
338 # moles/volume -> moles requires the solution volume
339 [substance] / [volume] -> [substance]: value * volume
340 [substance] -> [substance] / [volume]: value / volume
341
342 # moles/mass -> moles requires the solvent (usually water) mass
343 [substance] / [mass] -> [substance]: value * solvent_mass
344 [substance] -> [substance] / [mass]: value / solvent_mass
345
346 # moles/mass -> moles/volume require the solvent mass and the volume
347 [substance] / [mass] -> [substance]/[volume]: value * solvent_mass / volume
348 [substance] / [volume] -> [substance] / [mass]: value / solvent_mass * volume
349
350@end
321351
=== modified file 'pint/quantity.py'
--- pint/quantity.py 2014-09-05 23:26:05 +0000
+++ pint/quantity.py 2015-01-07 15:51:38 +0000
@@ -15,7 +15,8 @@
15import functools15import functools
1616
17from .formatting import remove_custom_flags17from .formatting import remove_custom_flags
18from .unit import DimensionalityError, UnitsContainer, UnitDefinition, UndefinedUnitError18from .unit import (DimensionalityError, OffsetUnitCalculusError,
19 UnitsContainer, UnitDefinition, UndefinedUnitError)
19from .compat import string_types, ndarray, np, _to_magnitude, long_type20from .compat import string_types, ndarray, np, _to_magnitude, long_type
20from .util import logger21from .util import logger
2122
@@ -48,6 +49,8 @@
48 # Both quantities are the same class and therefore from the same registry.49 # Both quantities are the same class and therefore from the same registry.
49 # (Each registry has its own Quantity class)50 # (Each registry has its own Quantity class)
50 return True51 return True
52 elif q1._REGISTRY is getattr(other, '_REGISTRY', None):
53 return True
51 elif isinstance(other, _Quantity):54 elif isinstance(other, _Quantity):
52 # The other object is a Quantity but from another registry.55 # The other object is a Quantity but from another registry.
53 raise ValueError('Cannot operate between quantities of different registries')56 raise ValueError('Cannot operate between quantities of different registries')
@@ -55,21 +58,8 @@
55 return False58 return False
5659
5760
58def _only_multiplicative_units(q):
59 """Check if the quantity has non-multiplicative units.
60 """
61
62 # Compound units are never multiplicative
63 if len(q.units) != 1:
64 return True
65
66 unit = list(q.units.keys())[0]
67
68 return q._REGISTRY._units[unit].is_multiplicative
69
70
71class _Quantity(object):61class _Quantity(object):
72 """Implements a class to describe a physical quantities:62 """Implements a class to describe a physical quantity:
73 the product of a numerical value and a unit of measurement.63 the product of a numerical value and a unit of measurement.
7464
75 :param value: value of the physical quantity to be created.65 :param value: value of the physical quantity to be created.
@@ -138,7 +128,7 @@
138 spec = spec or self.default_format128 spec = spec or self.default_format
139129
140 if '~' in spec:130 if '~' in spec:
141 units = UnitsContainer(dict((self._REGISTRY.get_symbol(key), value)131 units = UnitsContainer(dict((self._REGISTRY._get_symbol(key), value)
142 for key, value in self.units.items()))132 for key, value in self.units.items()))
143 spec = spec.replace('~', '')133 spec = spec.replace('~', '')
144 else:134 else:
@@ -304,15 +294,8 @@
304 :param op: operator function (e.g. operator.add, operator.isub)294 :param op: operator function (e.g. operator.add, operator.isub)
305 :type op: function295 :type op: function
306 """296 """
307 if _check(self, other):297 if not _check(self, other):
308 if not self.dimensionality == other.dimensionality:298 # other not from same Registry or not a Quantity
309 raise DimensionalityError(self.units, other.units,
310 self.dimensionality, other.dimensionality)
311 if self._units == other._units:
312 self._magnitude = op(self._magnitude, other._magnitude)
313 else:
314 self._magnitude = op(self._magnitude, other.to(self)._magnitude)
315 else:
316 try:299 try:
317 other_magnitude = _to_magnitude(other, self.force_ndarray)300 other_magnitude = _to_magnitude(other, self.force_ndarray)
318 except TypeError:301 except TypeError:
@@ -328,6 +311,75 @@
328 self._magnitude = op(self._magnitude, other_magnitude)311 self._magnitude = op(self._magnitude, other_magnitude)
329 else:312 else:
330 raise DimensionalityError(self.units, 'dimensionless')313 raise DimensionalityError(self.units, 'dimensionless')
314 return self
315
316 if not self.dimensionality == other.dimensionality:
317 raise DimensionalityError(self.units, other.units,
318 self.dimensionality,
319 other.dimensionality)
320
321 # Next we define some variables to make if-clauses more readable.
322 self_non_mul_units = self._get_non_multiplicative_units()
323 is_self_multiplicative = len(self_non_mul_units) == 0
324 if len(self_non_mul_units) == 1:
325 self_non_mul_unit = self_non_mul_units[0]
326 other_non_mul_units = other._get_non_multiplicative_units()
327 is_other_multiplicative = len(other_non_mul_units) == 0
328 if len(other_non_mul_units) == 1:
329 other_non_mul_unit = other_non_mul_units[0]
330
331 # Presence of non-multiplicative units gives rise to several cases.
332 if is_self_multiplicative and is_other_multiplicative:
333 if self._units == other._units:
334 self._magnitude = op(self._magnitude, other._magnitude)
335 # If only self has a delta unit, other determines unit of result.
336 elif self._get_delta_units() and not other._get_delta_units():
337 self._magnitude = op(self._convert_magnitude(other.units),
338 other._magnitude)
339 self._units = copy.copy(other.units)
340 else:
341 self._magnitude = op(self._magnitude,
342 other.to(self.units)._magnitude)
343
344 elif (op == operator.isub and len(self_non_mul_units) == 1
345 and self.units[self_non_mul_unit] == 1
346 and not other._has_compatible_delta(self_non_mul_unit)):
347 if self.units == other.units:
348 self._magnitude = op(self._magnitude, other._magnitude)
349 else:
350 self._magnitude = op(self._magnitude,
351 other.to(self.units)._magnitude)
352 self.units['delta_' + self_non_mul_unit
353 ] = self.units.pop(self_non_mul_unit)
354
355 elif (op == operator.isub and len(other_non_mul_units) == 1
356 and other.units[other_non_mul_unit] == 1
357 and not self._has_compatible_delta(other_non_mul_unit)):
358 # we convert to self directly since it is multiplicative
359 self._magnitude = op(self._magnitude,
360 other.to(self.units)._magnitude)
361
362 elif (len(self_non_mul_units) == 1
363 # order of the dimension of offset unit == 1 ?
364 and self._units[self_non_mul_unit] == 1
365 and other._has_compatible_delta(self_non_mul_unit)):
366 tu = copy.copy(self.units)
367 # Replace offset unit in self by the corresponding delta unit.
368 # This is done to prevent a shift by offset in the to()-call.
369 tu['delta_' + self_non_mul_unit] = tu.pop(self_non_mul_unit)
370 self._magnitude = op(self._magnitude, other.to(tu)._magnitude)
371 elif (len(other_non_mul_units) == 1
372 # order of the dimension of offset unit == 1 ?
373 and other._units[other_non_mul_unit] == 1
374 and self._has_compatible_delta(other_non_mul_unit)):
375 tu = copy.copy(other.units)
376 # Replace offset unit in other by the corresponding delta unit.
377 # This is done to prevent a shift by offset in the to()-call.
378 tu['delta_' + other_non_mul_unit] = tu.pop(other_non_mul_unit)
379 self._magnitude = op(self._convert_magnitude(tu), other._magnitude)
380 self._units = copy.copy(other.units)
381 else:
382 raise OffsetUnitCalculusError(self.units, other.units)
331383
332 return self384 return self
333385
@@ -339,17 +391,8 @@
339 :param op: operator function (e.g. operator.add, operator.isub)391 :param op: operator function (e.g. operator.add, operator.isub)
340 :type op: function392 :type op: function
341 """393 """
342 if _check(self, other):394 if not _check(self, other):
343 if not self.dimensionality == other.dimensionality:395 # other not from same Registry or not a Quantity
344 raise DimensionalityError(self.units, other.units,
345 self.dimensionality, other.dimensionality)
346 if self._units == other._units:
347 magnitude = op(self._magnitude, other._magnitude)
348 else:
349 magnitude = op(self._magnitude, other.to(self)._magnitude)
350
351 units = copy.copy(self.units)
352 else:
353 if _eq(other, 0, True):396 if _eq(other, 0, True):
354 # If the other value is 0 (but not Quantity 0)397 # If the other value is 0 (but not Quantity 0)
355 # do the operation without checking units.398 # do the operation without checking units.
@@ -364,6 +407,79 @@
364 _to_magnitude(other, self.force_ndarray))407 _to_magnitude(other, self.force_ndarray))
365 else:408 else:
366 raise DimensionalityError(self.units, 'dimensionless')409 raise DimensionalityError(self.units, 'dimensionless')
410 return self.__class__(magnitude, units)
411
412 if not self.dimensionality == other.dimensionality:
413 raise DimensionalityError(self.units, other.units,
414 self.dimensionality,
415 other.dimensionality)
416
417 # Next we define some variables to make if-clauses more readable.
418 self_non_mul_units = self._get_non_multiplicative_units()
419 is_self_multiplicative = len(self_non_mul_units) == 0
420 if len(self_non_mul_units) == 1:
421 self_non_mul_unit = self_non_mul_units[0]
422 other_non_mul_units = other._get_non_multiplicative_units()
423 is_other_multiplicative = len(other_non_mul_units) == 0
424 if len(other_non_mul_units) == 1:
425 other_non_mul_unit = other_non_mul_units[0]
426
427 # Presence of non-multiplicative units gives rise to several cases.
428 if is_self_multiplicative and is_other_multiplicative:
429 if self._units == other._units:
430 magnitude = op(self._magnitude, other._magnitude)
431 units = copy.copy(self.units)
432 # If only self has a delta unit, other determines unit of result.
433 elif self._get_delta_units() and not other._get_delta_units():
434 magnitude = op(self._convert_magnitude(other.units),
435 other._magnitude)
436 units = copy.copy(other.units)
437 else:
438 units = copy.copy(self.units)
439 magnitude = op(self._magnitude,
440 other.to(self.units).magnitude)
441
442 elif (op == operator.sub and len(self_non_mul_units) == 1
443 and self.units[self_non_mul_unit] == 1
444 and not other._has_compatible_delta(self_non_mul_unit)):
445 if self.units == other.units:
446 magnitude = op(self._magnitude, other._magnitude)
447 else:
448 magnitude = op(self._magnitude,
449 other.to(self.units)._magnitude)
450 units = copy.copy(self.units)
451 units['delta_' + self_non_mul_unit] = units.pop(self_non_mul_unit)
452
453 elif (op == operator.sub and len(other_non_mul_units) == 1
454 and other.units[other_non_mul_unit] == 1
455 and not self._has_compatible_delta(other_non_mul_unit)):
456 # we convert to self directly since it is multiplicative
457 magnitude = op(self._magnitude,
458 other.to(self.units)._magnitude)
459 units = copy.copy(self.units)
460
461 elif (len(self_non_mul_units) == 1
462 # order of the dimension of offset unit == 1 ?
463 and self._units[self_non_mul_unit] == 1
464 and other._has_compatible_delta(self_non_mul_unit)):
465 tu = copy.copy(self.units)
466 # Replace offset unit in self by the corresponding delta unit.
467 # This is done to prevent a shift by offset in the to()-call.
468 tu['delta_' + self_non_mul_unit] = tu.pop(self_non_mul_unit)
469 magnitude = op(self._magnitude, other.to(tu).magnitude)
470 units = copy.copy(self.units)
471 elif (len(other_non_mul_units) == 1
472 # order of the dimension of offset unit == 1 ?
473 and other._units[other_non_mul_unit] == 1
474 and self._has_compatible_delta(other_non_mul_unit)):
475 tu = copy.copy(other.units)
476 # Replace offset unit in other by the corresponding delta unit.
477 # This is done to prevent a shift by offset in the to()-call.
478 tu['delta_' + other_non_mul_unit] = tu.pop(other_non_mul_unit)
479 magnitude = op(self._convert_magnitude(tu), other._magnitude)
480 units = copy.copy(other.units)
481 else:
482 raise OffsetUnitCalculusError(self.units, other.units)
367483
368 return self.__class__(magnitude, units)484 return self.__class__(magnitude, units)
369485
@@ -403,24 +519,40 @@
403 if units_op is None:519 if units_op is None:
404 units_op = magnitude_op520 units_op = magnitude_op
405521
406 if self.__used:522 offset_units_self = self._get_non_multiplicative_units()
407 if not _only_multiplicative_units(self):523 no_offset_units_self = len(offset_units_self)
408 self.ito_base_units()
409 else:
410 self.__used = True
411524
412 if _check(self, other):525 if not _check(self, other):
413 if not _only_multiplicative_units(other):526 if not self._ok_for_muldiv(no_offset_units_self):
414 other = other.to_base_units()527 raise OffsetUnitCalculusError(self.units,
415 self._magnitude = magnitude_op(self._magnitude, other._magnitude)528 getattr(other, 'units', ''))
416 self._units = units_op(self._units, other._units)529 if len(offset_units_self) == 1:
417 else:530 if (self.units[offset_units_self[0]] != 1
531 or magnitude_op not in [operator.mul, operator.imul]):
532 raise OffsetUnitCalculusError(self.units,
533 getattr(other, 'units', ''))
418 try:534 try:
419 other_magnitude = _to_magnitude(other, self.force_ndarray)535 other_magnitude = _to_magnitude(other, self.force_ndarray)
420 except TypeError:536 except TypeError:
421 return NotImplemented537 return NotImplemented
422 self._magnitude = magnitude_op(self._magnitude, other_magnitude)538 self._magnitude = magnitude_op(self._magnitude, other_magnitude)
423 self._units = units_op(self._units, UnitsContainer())539 self._units = units_op(self._units, UnitsContainer())
540 return self
541
542 if not self._ok_for_muldiv(no_offset_units_self):
543 raise OffsetUnitCalculusError(self.units, other.units)
544 elif no_offset_units_self == 1 and len(self.units) == 1:
545 self.ito_base_units()
546
547 no_offset_units_other = len(other._get_non_multiplicative_units())
548
549 if not other._ok_for_muldiv(no_offset_units_other):
550 raise OffsetUnitCalculusError(self.units, other.units)
551 elif no_offset_units_other == 1 and len(other.units) == 1:
552 other.ito_base_units()
553
554 self._magnitude = magnitude_op(self._magnitude, other._magnitude)
555 self._units = units_op(self._units, other._units)
424556
425 return self557 return self
426558
@@ -437,27 +569,46 @@
437 if units_op is None:569 if units_op is None:
438 units_op = magnitude_op570 units_op = magnitude_op
439571
572 offset_units_self = self._get_non_multiplicative_units()
573 no_offset_units_self = len(offset_units_self)
574
575 if not _check(self, other):
576 if not self._ok_for_muldiv(no_offset_units_self):
577 raise OffsetUnitCalculusError(self.units,
578 getattr(other, 'units', ''))
579 if len(offset_units_self) == 1:
580 if (self.units[offset_units_self[0]] != 1
581 or magnitude_op not in [operator.mul, operator.imul]):
582 raise OffsetUnitCalculusError(self.units,
583 getattr(other, 'units', ''))
584 try:
585 other_magnitude = _to_magnitude(other, self.force_ndarray)
586 except TypeError:
587 return NotImplemented
588
589 magnitude = magnitude_op(self._magnitude, other_magnitude)
590 units = units_op(self._units, UnitsContainer())
591
592 return self.__class__(magnitude, units)
593
440 new_self = self594 new_self = self
441 if self.__used:595
442 if not _only_multiplicative_units(self):596 if not self._ok_for_muldiv(no_offset_units_self):
443 new_self = self.to_base_units()597 raise OffsetUnitCalculusError(self.units, other.units)
444598 elif no_offset_units_self == 1 and len(self.units) == 1:
445 if _check(self, other):599 new_self = self.to_base_units()
446 if not _only_multiplicative_units(other):600
447 other = other.to_base_units()601 no_offset_units_other = len(other._get_non_multiplicative_units())
448 magnitude = magnitude_op(new_self._magnitude, other._magnitude)602
449 units = units_op(new_self._units, other._units)603 if not other._ok_for_muldiv(no_offset_units_other):
450 else:604 raise OffsetUnitCalculusError(self.units, other.units)
451 try:605 elif no_offset_units_other == 1 and len(other.units) == 1:
452 other_magnitude = _to_magnitude(other, self.force_ndarray)606 other = other.to_base_units()
453 except TypeError:607
454 return NotImplemented608 magnitude = magnitude_op(new_self._magnitude, other._magnitude)
455 magnitude = magnitude_op(new_self._magnitude, other_magnitude)609 units = units_op(new_self._units, other._units)
456 units = units_op(new_self._units, UnitsContainer())610
457611 return self.__class__(magnitude, units)
458 ret = self.__class__(magnitude, units)
459 ret.__used = True
460 return ret
461612
462 def __imul__(self, other):613 def __imul__(self, other):
463 if not isinstance(self._magnitude, ndarray):614 if not isinstance(self._magnitude, ndarray):
@@ -493,6 +644,13 @@
493 other_magnitude = _to_magnitude(other, self.force_ndarray)644 other_magnitude = _to_magnitude(other, self.force_ndarray)
494 except TypeError:645 except TypeError:
495 return NotImplemented646 return NotImplemented
647
648 no_offset_units_self = len(self._get_non_multiplicative_units())
649 if not self._ok_for_muldiv(no_offset_units_self):
650 raise OffsetUnitCalculusError(self.units, '')
651 elif no_offset_units_self == 1 and len(self.units) == 1:
652 self = self.to_base_units()
653
496 return self.__class__(other_magnitude / self._magnitude, 1 / self._units)654 return self.__class__(other_magnitude / self._magnitude, 1 / self._units)
497655
498 def __rfloordiv__(self, other):656 def __rfloordiv__(self, other):
@@ -500,6 +658,13 @@
500 other_magnitude = _to_magnitude(other, self.force_ndarray)658 other_magnitude = _to_magnitude(other, self.force_ndarray)
501 except TypeError:659 except TypeError:
502 return NotImplemented660 return NotImplemented
661
662 no_offset_units_self = len(self._get_non_multiplicative_units())
663 if not self._ok_for_muldiv(no_offset_units_self):
664 raise OffsetUnitCalculusError(self.units, '')
665 elif no_offset_units_self == 1 and len(self.units) == 1:
666 self = self.to_base_units()
667
503 return self.__class__(other_magnitude // self._magnitude, 1 / self._units)668 return self.__class__(other_magnitude // self._magnitude, 1 / self._units)
504669
505 __div__ = __truediv__670 __div__ = __truediv__
@@ -515,10 +680,36 @@
515 except TypeError:680 except TypeError:
516 return NotImplemented681 return NotImplemented
517 else:682 else:
518 if not _only_multiplicative_units(self):683 if not self._ok_for_muldiv:
519 self.ito_base_units()684 raise OffsetUnitCalculusError(self.units)
685
686 if isinstance(getattr(other, '_magnitude', other), ndarray):
687 # arrays are refused as exponent, because they would create
688 # len(array) quanitites of len(set(array)) different units
689 if np.size(other) > 1:
690 raise DimensionalityError(self.units, 'dimensionless')
691
692 new_self = self
693 if other == 1:
694 return self
695 elif other == 0:
696 self._units = UnitsContainer()
697 else:
698 if not self._is_multiplicative:
699 if self._REGISTRY.autoconvert_offset_to_baseunit:
700 self.ito_base_units()
701 else:
702 raise OffsetUnitCalculusError(self.units)
703
704 if getattr(other, 'dimensionless', False):
705 other = other.to_base_units()
706 self._units **= other.magnitude
707 elif not getattr(other, 'dimensionless', True):
708 raise DimensionalityError(self.units, 'dimensionless')
709 else:
710 self._units **= other
711
520 self._magnitude **= _to_magnitude(other, self.force_ndarray)712 self._magnitude **= _to_magnitude(other, self.force_ndarray)
521 self._units **= other
522 return self713 return self
523714
524 def __pow__(self, other):715 def __pow__(self, other):
@@ -527,14 +718,51 @@
527 except TypeError:718 except TypeError:
528 return NotImplemented719 return NotImplemented
529 else:720 else:
721 if not self._ok_for_muldiv:
722 raise OffsetUnitCalculusError(self.units)
723
724 if isinstance(getattr(other, '_magnitude', other), ndarray):
725 # arrays are refused as exponent, because they would create
726 # len(array) quantities of len(set(array)) different units
727 if np.size(other) > 1:
728 raise DimensionalityError(self.units, 'dimensionless')
729
530 new_self = self730 new_self = self
531 if not _only_multiplicative_units(self):731 if other == 1:
532 new_self = self.to_base_units()732 return self
733 elif other == 0:
734 units = UnitsContainer()
735 else:
736 if not self._is_multiplicative:
737 if self._REGISTRY.autoconvert_offset_to_baseunit:
738 new_self = self.to_base_units()
739 else:
740 raise OffsetUnitCalculusError(self.units)
741
742 if getattr(other, 'dimensionless', False):
743 units = new_self._units ** other.to_base_units().magnitude
744 elif not getattr(other, 'dimensionless', True):
745 raise DimensionalityError(self.units, 'dimensionless')
746 else:
747 units = new_self._units ** other
533748
534 magnitude = new_self._magnitude ** _to_magnitude(other, self.force_ndarray)749 magnitude = new_self._magnitude ** _to_magnitude(other, self.force_ndarray)
535 units = new_self._units ** other
536 return self.__class__(magnitude, units)750 return self.__class__(magnitude, units)
537751
752 def __rpow__(self, other):
753 try:
754 other_magnitude = _to_magnitude(other, self.force_ndarray)
755 except TypeError:
756 return NotImplemented
757 else:
758 if not self.dimensionless:
759 raise DimensionalityError(self.units, 'dimensionless')
760 if isinstance(self._magnitude, ndarray):
761 if np.size(self._magnitude) > 1:
762 raise DimensionalityError(self.units, 'dimensionless')
763 new_self = self.to_base_units()
764 return other**new_self._magnitude
765
538 def __abs__(self):766 def __abs__(self):
539 return self.__class__(abs(self._magnitude), self._units)767 return self.__class__(abs(self._magnitude), self._units)
540768
@@ -798,7 +1026,10 @@
7981026
799 if isinstance(factor, self.__class__):1027 if isinstance(factor, self.__class__):
800 if not factor.dimensionless:1028 if not factor.dimensionless:
801 raise ValueError1029 raise DimensionalityError(value, self.units,
1030 extra_msg='. Assign a quantity with the same dimensionality or '
1031 'access the magnitude directly as '
1032 '`obj.magnitude[%s] = %s`' % (key, value))
802 self._magnitude[key] = factor.magnitude1033 self._magnitude[key] = factor.magnitude
803 else:1034 else:
804 self._magnitude[key] = factor1035 self._magnitude[key] = factor
@@ -961,3 +1192,58 @@
961 error = error * abs(self.magnitude)1192 error = error * abs(self.magnitude)
9621193
963 return self._REGISTRY.Measurement(copy.copy(self.magnitude), error, self.units)1194 return self._REGISTRY.Measurement(copy.copy(self.magnitude), error, self.units)
1195
1196 # methods/properties that help for math operations with offset units
1197 @property
1198 def _is_multiplicative(self):
1199 """Check if the Quantity object has only multiplicative units.
1200 """
1201 # XXX Turn this into a method/property of _Quantity?
1202 return not self._get_non_multiplicative_units()
1203
1204 def _get_non_multiplicative_units(self):
1205 """Return a list of the of non-multiplicative units of the Quantity object
1206 """
1207 offset_units = [unit for unit in self.units.keys()
1208 if not self._REGISTRY._units[unit].is_multiplicative]
1209 return offset_units
1210
1211 def _get_delta_units(self):
1212 """Return list of delta units ot the Quantity object
1213 """
1214 delta_units = [u for u in self.units.keys() if u.startswith("delta_")]
1215 return delta_units
1216
1217 def _has_compatible_delta(self, unit):
1218 """"Check if Quantity object has a delta_unit that is compatible with unit
1219 """
1220 deltas = self._get_delta_units()
1221 if 'delta_' + unit in deltas:
1222 return True
1223 else: # Look for delta units with same dimension as the offset unit
1224 offset_unit_dim = self._REGISTRY._units[unit].reference
1225 for d in deltas:
1226 if self._REGISTRY._units[d].reference == offset_unit_dim:
1227 return True
1228 return False
1229
1230 def _ok_for_muldiv(self, no_offset_units=None):
1231 """Checks if Quantity object can be multiplied or divided
1232
1233 :q: quantity object that is checked
1234 :no_offset_units: number of offset units in q
1235 """
1236 is_ok = True
1237 if no_offset_units is None:
1238 no_offset_units = len(self._get_non_multiplicative_units())
1239 if no_offset_units > 1:
1240 is_ok = False
1241 if no_offset_units == 1:
1242 if len(self._units) > 1:
1243 is_ok = False
1244 if (len(self._units) == 1
1245 and not self._REGISTRY.autoconvert_offset_to_baseunit):
1246 is_ok = False
1247 if next(iter(self._units.values())) != 1:
1248 is_ok = False
1249 return is_ok
9641250
=== added file 'pint/testsuite/parameterized.py'
--- pint/testsuite/parameterized.py 1970-01-01 00:00:00 +0000
+++ pint/testsuite/parameterized.py 2015-01-07 15:51:38 +0000
@@ -0,0 +1,152 @@
1# -*- coding: utf-8 -*-
2#
3# Adds Parameterized tests for Python's unittest module
4#
5# Code from: parameterizedtestcase, version: 0.1.0
6# Homepage: https://github.com/msabramo/python_unittest_parameterized_test_case
7# Author: Marc Abramowitz, email: marc@marc-abramowitz.com
8# License: MIT
9#
10# Fixed for to work in Python 2 & 3 with "add_metaclass" decorator from six
11# https://pypi.python.org/pypi/six
12# Author: Benjamin Peterson
13# License: MIT
14#
15# Use like this:
16#
17# from parameterizedtestcase import ParameterizedTestCase
18#
19# class MyTests(ParameterizedTestCase):
20# @ParameterizedTestCase.parameterize(
21# ("input", "expected_output"),
22# [
23# ("2+4", 6),
24# ("3+5", 8),
25# ("6*9", 54),
26# ]
27# )
28# def test_eval(self, input, expected_output):
29# self.assertEqual(eval(input), expected_output)
30
31try:
32 import unittest2 as unittest
33except ImportError: # pragma: no cover
34 import unittest
35
36from functools import wraps
37import collections
38
39
40def add_metaclass(metaclass):
41 """Class decorator for creating a class with a metaclass."""
42 def wrapper(cls):
43 orig_vars = cls.__dict__.copy()
44 orig_vars.pop('__dict__', None)
45 orig_vars.pop('__weakref__', None)
46 slots = orig_vars.get('__slots__')
47 if slots is not None:
48 if isinstance(slots, str):
49 slots = [slots]
50 for slots_var in slots:
51 orig_vars.pop(slots_var)
52 return metaclass(cls.__name__, cls.__bases__, orig_vars)
53 return wrapper
54
55
56def augment_method_docstring(method, new_class_dict, classname,
57 param_names, param_values, new_method):
58 param_assignments_str = '; '.join(
59 ['%s = %s' % (k, v) for (k, v) in zip(param_names, param_values)])
60 extra_doc = "%s (%s.%s) [with %s] " % (
61 method.__name__, new_class_dict.get('__module__', '<module>'),
62 classname, param_assignments_str)
63
64 try:
65 new_method.__doc__ = extra_doc + new_method.__doc__
66 except TypeError: # Catches when new_method.__doc__ is None
67 new_method.__doc__ = extra_doc
68
69
70class ParameterizedTestCaseMetaClass(type):
71 method_counter = {}
72
73 def __new__(meta, classname, bases, class_dict):
74 new_class_dict = {}
75
76 for attr_name, attr_value in list(class_dict.items()):
77 if isinstance(attr_value, collections.Callable) and hasattr(attr_value, 'param_names'):
78 # print("Processing attr_name = %r; attr_value = %r" % (
79 # attr_name, attr_value))
80
81 method = attr_value
82 param_names = attr_value.param_names
83 data = attr_value.data
84 func_name_format = attr_value.func_name_format
85
86 meta.process_method(
87 classname, method, param_names, data, new_class_dict,
88 func_name_format)
89 else:
90 new_class_dict[attr_name] = attr_value
91
92 return type.__new__(meta, classname, bases, new_class_dict)
93
94 @classmethod
95 def process_method(
96 cls, classname, method, param_names, data, new_class_dict,
97 func_name_format):
98 method_counter = cls.method_counter
99
100 for param_values in data:
101 new_method = cls.new_method(method, param_values)
102 method_counter[method.__name__] = \
103 method_counter.get(method.__name__, 0) + 1
104 case_data = dict(list(zip(param_names, param_values)))
105 case_data['func_name'] = method.__name__
106 case_data['case_num'] = method_counter[method.__name__]
107
108 new_method.__name__ = func_name_format.format(**case_data)
109
110 augment_method_docstring(
111 method, new_class_dict, classname,
112 param_names, param_values, new_method)
113 new_class_dict[new_method.__name__] = new_method
114
115 @classmethod
116 def new_method(cls, method, param_values):
117 @wraps(method)
118 def new_method(self):
119 return method(self, *param_values)
120
121 return new_method
122
123@add_metaclass(ParameterizedTestCaseMetaClass)
124class ParameterizedTestMixin(object):
125 @classmethod
126 def parameterize(cls, param_names, data,
127 func_name_format='{func_name}_{case_num:05d}'):
128 """Decorator for parameterizing a test method - example:
129
130 @ParameterizedTestCase.parameterize(
131 ("isbn", "expected_title"), [
132 ("0262033844", "Introduction to Algorithms"),
133 ("0321558146", "Campbell Essential Biology")])
134
135 """
136
137 def decorator(func):
138 @wraps(func)
139 def newfunc(*arg, **kwargs):
140 return func(*arg, **kwargs)
141
142 newfunc.param_names = param_names
143 newfunc.data = data
144 newfunc.func_name_format = func_name_format
145
146 return newfunc
147
148 return decorator
149
150@add_metaclass(ParameterizedTestCaseMetaClass)
151class ParameterizedTestCase(unittest.TestCase, ParameterizedTestMixin):
152 pass
0153
=== modified file 'pint/testsuite/test_issues.py'
--- pint/testsuite/test_issues.py 2014-09-05 23:26:05 +0000
+++ pint/testsuite/test_issues.py 2015-01-07 15:51:38 +0000
@@ -16,6 +16,9 @@
1616
17 FORCE_NDARRAY = False17 FORCE_NDARRAY = False
1818
19 def setup(self):
20 self.ureg.autoconvert_offset_to_baseunit = False
21
19 @unittest.expectedFailure22 @unittest.expectedFailure
20 def test_issue25(self):23 def test_issue25(self):
21 x = ParserHelper.from_string('10 %')24 x = ParserHelper.from_string('10 %')
@@ -92,9 +95,9 @@
92 def test_issue66b(self):95 def test_issue66b(self):
93 ureg = UnitRegistry()96 ureg = UnitRegistry()
94 self.assertEqual(ureg.get_base_units(ureg.kelvin.units),97 self.assertEqual(ureg.get_base_units(ureg.kelvin.units),
95 (None, UnitsContainer({'kelvin': 1})))98 (1.0, UnitsContainer({'kelvin': 1})))
96 self.assertEqual(ureg.get_base_units(ureg.degC.units),99 self.assertEqual(ureg.get_base_units(ureg.degC.units),
97 (None, UnitsContainer({'kelvin': 1})))100 (1.0, UnitsContainer({'kelvin': 1})))
98101
99 def test_issue69(self):102 def test_issue69(self):
100 ureg = UnitRegistry()103 ureg = UnitRegistry()
@@ -127,11 +130,12 @@
127 self.assertQuantityAlmostEqual(va.to_base_units(), vb.to_base_units())130 self.assertQuantityAlmostEqual(va.to_base_units(), vb.to_base_units())
128131
129 def test_issue86(self):132 def test_issue86(self):
133 ureg = self.ureg
134 ureg.autoconvert_offset_to_baseunit = True
135
130 def parts(q):136 def parts(q):
131 return q.magnitude, q.units137 return q.magnitude, q.units
132138
133 ureg = UnitRegistry()
134
135 q1 = 10. * ureg.degC139 q1 = 10. * ureg.degC
136 q2 = 10. * ureg.kelvin140 q2 = 10. * ureg.kelvin
137141
@@ -158,7 +162,6 @@
158 self.assertEqual(parts(q1 / q3), (k1m / q3m, k1u / q3u))162 self.assertEqual(parts(q1 / q3), (k1m / q3m, k1u / q3u))
159 self.assertEqual(parts(q3 * q1), (q3m * k1m, q3u * k1u))163 self.assertEqual(parts(q3 * q1), (q3m * k1m, q3u * k1u))
160 self.assertEqual(parts(q3 / q1), (q3m / k1m, q3u / k1u))164 self.assertEqual(parts(q3 / q1), (q3m / k1m, q3u / k1u))
161 self.assertEqual(parts(q1 ** 1), (k1m ** 1, k1u ** 1))
162 self.assertEqual(parts(q1 ** -1), (k1m ** -1, k1u ** -1))165 self.assertEqual(parts(q1 ** -1), (k1m ** -1, k1u ** -1))
163 self.assertEqual(parts(q1 ** 2), (k1m ** 2, k1u ** 2))166 self.assertEqual(parts(q1 ** 2), (k1m ** 2, k1u ** 2))
164 self.assertEqual(parts(q1 ** -2), (k1m ** -2, k1u ** -2))167 self.assertEqual(parts(q1 ** -2), (k1m ** -2, k1u ** -2))
@@ -177,8 +180,10 @@
177 self.assertQuantityAlmostEqual(v1.to_base_units(), v2)180 self.assertQuantityAlmostEqual(v1.to_base_units(), v2)
178 self.assertQuantityAlmostEqual(v1.to_base_units(), v2.to_base_units())181 self.assertQuantityAlmostEqual(v1.to_base_units(), v2.to_base_units())
179182
183 @unittest.expectedFailure
180 def test_issue86c(self):184 def test_issue86c(self):
181 ureg = self.ureg185 ureg = self.ureg
186 ureg.autoconvert_offset_to_baseunit = True
182 T = ureg.degC187 T = ureg.degC
183 T = 100. * T188 T = 100. * T
184 self.assertQuantityAlmostEqual(ureg.k*2*T, ureg.k*(2*T))189 self.assertQuantityAlmostEqual(ureg.k*2*T, ureg.k*(2*T))
185190
=== modified file 'pint/testsuite/test_numpy.py'
--- pint/testsuite/test_numpy.py 2014-09-05 23:26:05 +0000
+++ pint/testsuite/test_numpy.py 2015-01-07 15:51:38 +0000
@@ -2,8 +2,13 @@
22
3from __future__ import division, unicode_literals, print_function, absolute_import3from __future__ import division, unicode_literals, print_function, absolute_import
44
5import copy
6import operator as op
7
8from pint import DimensionalityError
5from pint.compat import np, unittest9from pint.compat import np, unittest
6from pint.testsuite import QuantityTestCase, helpers10from pint.testsuite import QuantityTestCase, helpers
11from pint.testsuite.test_umath import TestUFuncs
712
813
9@helpers.requires_numpy()14@helpers.requires_numpy()
@@ -189,9 +194,11 @@
189 q[0] = 1*self.ureg.m194 q[0] = 1*self.ureg.m
190 self.assertQuantityEqual(q, [[1,1],[3,4]]*self.ureg.m)195 self.assertQuantityEqual(q, [[1,1],[3,4]]*self.ureg.m)
191196
192 q[0] = (1,2)*self.ureg.m197 q = self.q.copy()
193 self.assertQuantityEqual(q, self.q)198 q.__setitem__(Ellipsis, 1*self.ureg.m)
199 self.assertQuantityEqual(q, [[1,1],[1,1]]*self.ureg.m)
194200
201 q = self.q.copy()
195 q[:] = 1*self.ureg.m202 q[:] = 1*self.ureg.m
196 self.assertQuantityEqual(q, [[1,1],[1,1]]*self.ureg.m)203 self.assertQuantityEqual(q, [[1,1],[1,1]]*self.ureg.m)
197204
@@ -239,58 +246,68 @@
239 self.assertQuantityEqual(u == 1, u.magnitude == 1)246 self.assertQuantityEqual(u == 1, u.magnitude == 1)
240247
241248
242from pint.testsuite.test_umath import TestUFuncs249@helpers.requires_numpy()
243@unittest.skip250class TestNumpyNeedsSubclassing(TestUFuncs):
244class TestNumpyNotSupported(TestUFuncs):
245251
246 FORCE_NDARRAY = True252 FORCE_NDARRAY = True
247253
254 @property
255 def q(self):
256 return [1. ,2., 3., 4.] * self.ureg.J
248257
258 @unittest.expectedFailure
249 def test_unwrap(self):259 def test_unwrap(self):
250 """unwrap depends on diff260 """unwrap depends on diff
251 """261 """
252 self.assertEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi])262 self.assertQuantityEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi])
253 self.assertEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg)263 self.assertQuantityEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg)
254264
265 @unittest.expectedFailure
255 def test_trapz(self):266 def test_trapz(self):
256 """Units are erased by asanyarray, Quantity does not inherit from NDArray267 """Units are erased by asanyarray, Quantity does not inherit from NDArray
257 """268 """
258 self.assertEqual(np.trapz(self.q, dx = 1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m)269 self.assertQuantityEqual(np.trapz(self.q, dx=1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m)
259270
271 @unittest.expectedFailure
260 def test_diff(self):272 def test_diff(self):
261 """Units are erased by asanyarray, Quantity does not inherit from NDArray273 """Units are erased by asanyarray, Quantity does not inherit from NDArray
262 """274 """
263 self.assertQuantityEqual(np.diff(self.q, 1), [1, 1, 1] * self.ureg.J)275 self.assertQuantityEqual(np.diff(self.q, 1), [1, 1, 1] * self.ureg.J)
264276
277 @unittest.expectedFailure
265 def test_ediff1d(self):278 def test_ediff1d(self):
266 """Units are erased by asanyarray, Quantity does not inherit from NDArray279 """Units are erased by asanyarray, Quantity does not inherit from NDArray
267 """280 """
268 self.assertEqual(np.ediff1d(self.q, 1), [1, 1, 1] * self.ureg.J)281 self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.J), [1, 1, 1] * self.ureg.J)
269282
283 @unittest.expectedFailure
270 def test_fix(self):284 def test_fix(self):
271 """Units are erased by asanyarray, Quantity does not inherit from NDArray285 """Units are erased by asanyarray, Quantity does not inherit from NDArray
272 """286 """
273 self.assertEqual(np.fix(3.14 * self.ureg.m), 3.0 * self.ureg.m)287 self.assertQuantityEqual(np.fix(3.14 * self.ureg.m), 3.0 * self.ureg.m)
274 self.assertEqual(np.fix(3.0 * self.ureg.m), 3.0 * self.ureg.m)288 self.assertQuantityEqual(np.fix(3.0 * self.ureg.m), 3.0 * self.ureg.m)
275 self.assertQuantityEqual(289 self.assertQuantityEqual(
276 np.fix([2.1, 2.9, -2.1, -2.9] * self.ureg.m),290 np.fix([2.1, 2.9, -2.1, -2.9] * self.ureg.m),
277 [2., 2., -2., -2.] * self.ureg.m291 [2., 2., -2., -2.] * self.ureg.m
278 )292 )
279293
294 @unittest.expectedFailure
280 def test_gradient(self):295 def test_gradient(self):
281 """shape is a property not a function296 """shape is a property not a function
282 """297 """
283 l = np.gradient([[1,1],[3,4]] * self.ureg.J, 1 * self.ureg.m)298 l = np.gradient([[1,1],[3,4]] * self.ureg.J, 1 * self.ureg.m)
284 self.assertEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.J / self.ureg.m)299 self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.J / self.ureg.m)
285 self.assertEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.J / self.ureg.m)300 self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.J / self.ureg.m)
286301
302 @unittest.expectedFailure
287 def test_cross(self):303 def test_cross(self):
288 """Units are erased by asarray, Quantity does not inherit from NDArray304 """Units are erased by asarray, Quantity does not inherit from NDArray
289 """305 """
290 a = [3,-3, 1] * self.ureg.kPa306 a = [[3,-3, 1]] * self.ureg.kPa
291 b = [4, 9, 2] * self.ureg.m**2307 b = [[4, 9, 2]] * self.ureg.m**2
292 self.assertQuantityEqual(np.cross(a,b), [-15,-2,39]*self.ureg.kPa*self.ureg.m**2)308 self.assertQuantityEqual(np.cross(a, b), [-15, -2, 39] * self.ureg.kPa * self.ureg.m**2)
293309
310 @unittest.expectedFailure
294 def test_power(self):311 def test_power(self):
295 """This is not supported as different elements might end up with different units312 """This is not supported as different elements might end up with different units
296313
@@ -302,6 +319,7 @@
302 (self.qless, np.asarray([1., 2, 3, 4])),319 (self.qless, np.asarray([1., 2, 3, 4])),
303 (self.q2, ),)320 (self.q2, ),)
304321
322 @unittest.expectedFailure
305 def test_ones_like(self):323 def test_ones_like(self):
306 """Units are erased by emptyarra, Quantity does not inherit from NDArray324 """Units are erased by emptyarra, Quantity does not inherit from NDArray
307 """325 """
@@ -385,3 +403,39 @@
385 (self.qless, 2),403 (self.qless, 2),
386 (self.q1, self.q2, self.qs, ),404 (self.q1, self.q2, self.qs, ),
387 'same')405 'same')
406
407
408class TestNDArrayQunatityMath(QuantityTestCase):
409
410 @helpers.requires_numpy()
411 def test_exponentiation_array_exp(self):
412 arr = np.array(range(3), dtype=np.float)
413 q = self.Q_(arr, None)
414
415 for op_ in [op.pow, op.ipow]:
416 q_cp = copy.copy(q)
417 self.assertRaises(DimensionalityError, op_, 2., q_cp)
418 arr_cp = copy.copy(arr)
419 arr_cp = copy.copy(arr)
420 q_cp = copy.copy(q)
421 self.assertRaises(DimensionalityError, op_, q_cp, arr_cp)
422 q_cp = copy.copy(q)
423 q2_cp = copy.copy(q)
424 self.assertRaises(DimensionalityError, op_, q_cp, q2_cp)
425
426 @unittest.expectedFailure
427 @helpers.requires_numpy()
428 def test_exponentiation_array_exp_2(self):
429 arr = np.array(range(3), dtype=np.float)
430 #q = self.Q_(copy.copy(arr), None)
431 q = self.Q_(copy.copy(arr), 'meter')
432 arr_cp = copy.copy(arr)
433 q_cp = copy.copy(q)
434 # this fails as expected since numpy 1.8.0 but...
435 self.assertRaises(DimensionalityError, op.pow, arr_cp, q_cp)
436 # ..not for op.ipow !
437 # q_cp is treated as if it is an array. The units are ignored.
438 # _Quantity.__ipow__ is never called
439 arr_cp = copy.copy(arr)
440 q_cp = copy.copy(q)
441 self.assertRaises(DimensionalityError, op.ipow, arr_cp, q_cp)
388442
=== modified file 'pint/testsuite/test_quantity.py'
--- pint/testsuite/test_quantity.py 2014-09-05 23:26:05 +0000
+++ pint/testsuite/test_quantity.py 2015-01-07 15:51:38 +0000
@@ -6,10 +6,11 @@
6import math6import math
7import operator as op7import operator as op
88
9from pint import DimensionalityError, UnitRegistry9from pint import DimensionalityError, OffsetUnitCalculusError, UnitRegistry
10from pint.unit import UnitsContainer10from pint.unit import UnitsContainer
11from pint.compat import string_types, PYTHON3, np11from pint.compat import string_types, PYTHON3, np, unittest
12from pint.testsuite import QuantityTestCase, helpers12from pint.testsuite import QuantityTestCase, helpers
13from pint.testsuite.parameterized import ParameterizedTestCase
1314
1415
15class TestQuantity(QuantityTestCase):16class TestQuantity(QuantityTestCase):
@@ -149,7 +150,7 @@
149 # Conversions with single units take a different codepath than150 # Conversions with single units take a different codepath than
150 # Conversions with more than one unit.151 # Conversions with more than one unit.
151 src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1)152 src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1)
152 src_dst2 = UnitsContainer(meter=1, seconds=-1), UnitsContainer(inch=1, minutes=-1)153 src_dst2 = UnitsContainer(meter=1, second=-1), UnitsContainer(inch=1, minute=-1)
153 for src, dst in (src_dst1, src_dst2):154 for src, dst in (src_dst1, src_dst2):
154 a = np.ones((3, 1))155 a = np.ones((3, 1))
155 ac = np.ones((3, 1))156 ac = np.ones((3, 1))
@@ -209,14 +210,12 @@
209210
210211
211 def test_offset_delta(self):212 def test_offset_delta(self):
212 self.assertQuantityAlmostEqual(self.Q_(0, 'delta_kelvin').to('delta_kelvin'), self.Q_(0, 'delta_kelvin'))213 self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degC').to('kelvin'), self.Q_(0, 'kelvin'))
213 self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degC').to('delta_kelvin'), self.Q_(0, 'delta_kelvin'))214 self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degF').to('kelvin'), self.Q_(0, 'kelvin'), rtol=0.01)
214 self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degF').to('delta_kelvin'), self.Q_(0, 'delta_kelvin'), rtol=0.01)
215215
216 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_kelvin').to('delta_kelvin'), self.Q_(100, 'delta_kelvin'))216 self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('delta_degC'), self.Q_(100, 'delta_degC'))
217 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_kelvin').to('delta_degC'), self.Q_(100, 'delta_degC'))217 self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01)
218 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_kelvin').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01)218 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('kelvin'), self.Q_(55.55555556, 'kelvin'), rtol=0.01)
219 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('delta_kelvin'), self.Q_(55.55555556, 'delta_kelvin'), rtol=0.01)
220 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degC').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01)219 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degC').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01)
221 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('delta_degC'), self.Q_(55.55555556, 'delta_degC'), rtol=0.01)220 self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('delta_degC'), self.Q_(55.55555556, 'delta_degC'), rtol=0.01)
222221
@@ -450,3 +449,547 @@
450 from pint import _DEFAULT_REGISTRY449 from pint import _DEFAULT_REGISTRY
451 cls.ureg = _DEFAULT_REGISTRY450 cls.ureg = _DEFAULT_REGISTRY
452 cls.Q_ = cls.ureg.Quantity451 cls.Q_ = cls.ureg.Quantity
452
453
454class TestOffsetUnitMath(QuantityTestCase, ParameterizedTestCase):
455
456 def setup(self):
457 self.ureg.autoconvert_offset_to_baseunit = False
458 self.ureg.default_as_delta = True
459
460 additions = [
461 # --- input tuple -------------------- | -- expected result --
462 (((100, 'kelvin'), (10, 'kelvin')), (110, 'kelvin')),
463 (((100, 'kelvin'), (10, 'degC')), 'error'),
464 (((100, 'kelvin'), (10, 'degF')), 'error'),
465 (((100, 'kelvin'), (10, 'degR')), (105.56, 'kelvin')),
466 (((100, 'kelvin'), (10, 'delta_degC')), (110, 'kelvin')),
467 (((100, 'kelvin'), (10, 'delta_degF')), (105.56, 'kelvin')),
468
469 (((100, 'degC'), (10, 'kelvin')), 'error'),
470 (((100, 'degC'), (10, 'degC')), 'error'),
471 (((100, 'degC'), (10, 'degF')), 'error'),
472 (((100, 'degC'), (10, 'degR')), 'error'),
473 (((100, 'degC'), (10, 'delta_degC')), (110, 'degC')),
474 (((100, 'degC'), (10, 'delta_degF')), (105.56, 'degC')),
475
476 (((100, 'degF'), (10, 'kelvin')), 'error'),
477 (((100, 'degF'), (10, 'degC')), 'error'),
478 (((100, 'degF'), (10, 'degF')), 'error'),
479 (((100, 'degF'), (10, 'degR')), 'error'),
480 (((100, 'degF'), (10, 'delta_degC')), (118, 'degF')),
481 (((100, 'degF'), (10, 'delta_degF')), (110, 'degF')),
482
483 (((100, 'degR'), (10, 'kelvin')), (118, 'degR')),
484 (((100, 'degR'), (10, 'degC')), 'error'),
485 (((100, 'degR'), (10, 'degF')), 'error'),
486 (((100, 'degR'), (10, 'degR')), (110, 'degR')),
487 (((100, 'degR'), (10, 'delta_degC')), (118, 'degR')),
488 (((100, 'degR'), (10, 'delta_degF')), (110, 'degR')),
489
490 (((100, 'delta_degC'), (10, 'kelvin')), (110, 'kelvin')),
491 (((100, 'delta_degC'), (10, 'degC')), (110, 'degC')),
492 (((100, 'delta_degC'), (10, 'degF')), (190, 'degF')),
493 (((100, 'delta_degC'), (10, 'degR')), (190, 'degR')),
494 (((100, 'delta_degC'), (10, 'delta_degC')), (110, 'delta_degC')),
495 (((100, 'delta_degC'), (10, 'delta_degF')), (105.56, 'delta_degC')),
496
497 (((100, 'delta_degF'), (10, 'kelvin')), (65.56, 'kelvin')),
498 (((100, 'delta_degF'), (10, 'degC')), (65.56, 'degC')),
499 (((100, 'delta_degF'), (10, 'degF')), (110, 'degF')),
500 (((100, 'delta_degF'), (10, 'degR')), (110, 'degR')),
501 (((100, 'delta_degF'), (10, 'delta_degC')), (118, 'delta_degF')),
502 (((100, 'delta_degF'), (10, 'delta_degF')), (110, 'delta_degF')),
503 ]
504
505 @ParameterizedTestCase.parameterize(("input", "expected_output"),
506 additions)
507 def test_addition(self, input_tuple, expected):
508 self.ureg.autoconvert_offset_to_baseunit = False
509 qin1, qin2 = input_tuple
510 q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
511 # update input tuple with new values to have correct values on failure
512 input_tuple = q1, q2
513 if expected == 'error':
514 self.assertRaises(OffsetUnitCalculusError, op.add, q1, q2)
515 else:
516 expected = self.Q_(*expected)
517 self.assertEqual(op.add(q1, q2).units, expected.units)
518 self.assertQuantityAlmostEqual(op.add(q1, q2), expected,
519 atol=0.01)
520
521 @helpers.requires_numpy()
522 @ParameterizedTestCase.parameterize(("input", "expected_output"),
523 additions)
524 def test_inplace_addition(self, input_tuple, expected):
525 self.ureg.autoconvert_offset_to_baseunit = False
526 (q1v, q1u), (q2v, q2u) = input_tuple
527 # update input tuple with new values to have correct values on failure
528 input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
529 (np.array([q2v]*2, dtype=np.float), q2u))
530 Q_ = self.Q_
531 qin1, qin2 = input_tuple
532 q1, q2 = Q_(*qin1), Q_(*qin2)
533 q1_cp = copy.copy(q1)
534 if expected == 'error':
535 self.assertRaises(OffsetUnitCalculusError, op.iadd, q1_cp, q2)
536 else:
537 expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
538 self.assertEqual(op.iadd(q1_cp, q2).units, Q_(*expected).units)
539 q1_cp = copy.copy(q1)
540 self.assertQuantityAlmostEqual(op.iadd(q1_cp, q2), Q_(*expected),
541 atol=0.01)
542
543 subtractions = [
544 (((100, 'kelvin'), (10, 'kelvin')), (90, 'kelvin')),
545 (((100, 'kelvin'), (10, 'degC')), (-183.15, 'kelvin')),
546 (((100, 'kelvin'), (10, 'degF')), (-160.93, 'kelvin')),
547 (((100, 'kelvin'), (10, 'degR')), (94.44, 'kelvin')),
548 (((100, 'kelvin'), (10, 'delta_degC')), (90, 'kelvin')),
549 (((100, 'kelvin'), (10, 'delta_degF')), (94.44, 'kelvin')),
550
551 (((100, 'degC'), (10, 'kelvin')), (363.15, 'delta_degC')),
552 (((100, 'degC'), (10, 'degC')), (90, 'delta_degC')),
553 (((100, 'degC'), (10, 'degF')), (112.22, 'delta_degC')),
554 (((100, 'degC'), (10, 'degR')), (367.59, 'delta_degC')),
555 (((100, 'degC'), (10, 'delta_degC')), (90, 'degC')),
556 (((100, 'degC'), (10, 'delta_degF')), (94.44, 'degC')),
557
558 (((100, 'degF'), (10, 'kelvin')), (541.67, 'delta_degF')),
559 (((100, 'degF'), (10, 'degC')), (50, 'delta_degF')),
560 (((100, 'degF'), (10, 'degF')), (90, 'delta_degF')),
561 (((100, 'degF'), (10, 'degR')), (549.67, 'delta_degF')),
562 (((100, 'degF'), (10, 'delta_degC')), (82, 'degF')),
563 (((100, 'degF'), (10, 'delta_degF')), (90, 'degF')),
564
565 (((100, 'degR'), (10, 'kelvin')), (82, 'degR')),
566 (((100, 'degR'), (10, 'degC')), (-409.67, 'degR')),
567 (((100, 'degR'), (10, 'degF')), (-369.67, 'degR')),
568 (((100, 'degR'), (10, 'degR')), (90, 'degR')),
569 (((100, 'degR'), (10, 'delta_degC')), (82, 'degR')),
570 (((100, 'degR'), (10, 'delta_degF')), (90, 'degR')),
571
572 (((100, 'delta_degC'), (10, 'kelvin')), (90, 'kelvin')),
573 (((100, 'delta_degC'), (10, 'degC')), (90, 'degC')),
574 (((100, 'delta_degC'), (10, 'degF')), (170, 'degF')),
575 (((100, 'delta_degC'), (10, 'degR')), (170, 'degR')),
576 (((100, 'delta_degC'), (10, 'delta_degC')), (90, 'delta_degC')),
577 (((100, 'delta_degC'), (10, 'delta_degF')), (94.44, 'delta_degC')),
578
579 (((100, 'delta_degF'), (10, 'kelvin')), (45.56, 'kelvin')),
580 (((100, 'delta_degF'), (10, 'degC')), (45.56, 'degC')),
581 (((100, 'delta_degF'), (10, 'degF')), (90, 'degF')),
582 (((100, 'delta_degF'), (10, 'degR')), (90, 'degR')),
583 (((100, 'delta_degF'), (10, 'delta_degC')), (82, 'delta_degF')),
584 (((100, 'delta_degF'), (10, 'delta_degF')), (90, 'delta_degF')),
585 ]
586
587 @ParameterizedTestCase.parameterize(("input", "expected_output"),
588 subtractions)
589 def test_subtraction(self, input_tuple, expected):
590 self.ureg.autoconvert_offset_to_baseunit = False
591 qin1, qin2 = input_tuple
592 q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
593 input_tuple = q1, q2
594 if expected == 'error':
595 self.assertRaises(OffsetUnitCalculusError, op.sub, q1, q2)
596 else:
597 expected = self.Q_(*expected)
598 self.assertEqual(op.sub(q1, q2).units, expected.units)
599 self.assertQuantityAlmostEqual(op.sub(q1, q2), expected,
600 atol=0.01)
601
602# @unittest.expectedFailure
603 @helpers.requires_numpy()
604 @ParameterizedTestCase.parameterize(("input", "expected_output"),
605 subtractions)
606 def test_inplace_subtraction(self, input_tuple, expected):
607 self.ureg.autoconvert_offset_to_baseunit = False
608 (q1v, q1u), (q2v, q2u) = input_tuple
609 # update input tuple with new values to have correct values on failure
610 input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
611 (np.array([q2v]*2, dtype=np.float), q2u))
612 Q_ = self.Q_
613 qin1, qin2 = input_tuple
614 q1, q2 = Q_(*qin1), Q_(*qin2)
615 q1_cp = copy.copy(q1)
616 if expected == 'error':
617 self.assertRaises(OffsetUnitCalculusError, op.isub, q1_cp, q2)
618 else:
619 expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
620 self.assertEqual(op.isub(q1_cp, q2).units, Q_(*expected).units)
621 q1_cp = copy.copy(q1)
622 self.assertQuantityAlmostEqual(op.isub(q1_cp, q2), Q_(*expected),
623 atol=0.01)
624
625 multiplications = [
626 (((100, 'kelvin'), (10, 'kelvin')), (1000, 'kelvin**2')),
627 (((100, 'kelvin'), (10, 'degC')), 'error'),
628 (((100, 'kelvin'), (10, 'degF')), 'error'),
629 (((100, 'kelvin'), (10, 'degR')), (1000, 'kelvin*degR')),
630 (((100, 'kelvin'), (10, 'delta_degC')), (1000, 'kelvin*delta_degC')),
631 (((100, 'kelvin'), (10, 'delta_degF')), (1000, 'kelvin*delta_degF')),
632
633 (((100, 'degC'), (10, 'kelvin')), 'error'),
634 (((100, 'degC'), (10, 'degC')), 'error'),
635 (((100, 'degC'), (10, 'degF')), 'error'),
636 (((100, 'degC'), (10, 'degR')), 'error'),
637 (((100, 'degC'), (10, 'delta_degC')), 'error'),
638 (((100, 'degC'), (10, 'delta_degF')), 'error'),
639
640 (((100, 'degF'), (10, 'kelvin')), 'error'),
641 (((100, 'degF'), (10, 'degC')), 'error'),
642 (((100, 'degF'), (10, 'degF')), 'error'),
643 (((100, 'degF'), (10, 'degR')), 'error'),
644 (((100, 'degF'), (10, 'delta_degC')), 'error'),
645 (((100, 'degF'), (10, 'delta_degF')), 'error'),
646
647 (((100, 'degR'), (10, 'kelvin')), (1000, 'degR*kelvin')),
648 (((100, 'degR'), (10, 'degC')), 'error'),
649 (((100, 'degR'), (10, 'degF')), 'error'),
650 (((100, 'degR'), (10, 'degR')), (1000, 'degR**2')),
651 (((100, 'degR'), (10, 'delta_degC')), (1000, 'degR*delta_degC')),
652 (((100, 'degR'), (10, 'delta_degF')), (1000, 'degR*delta_degF')),
653
654 (((100, 'delta_degC'), (10, 'kelvin')), (1000, 'delta_degC*kelvin')),
655 (((100, 'delta_degC'), (10, 'degC')), 'error'),
656 (((100, 'delta_degC'), (10, 'degF')), 'error'),
657 (((100, 'delta_degC'), (10, 'degR')), (1000, 'delta_degC*degR')),
658 (((100, 'delta_degC'), (10, 'delta_degC')), (1000, 'delta_degC**2')),
659 (((100, 'delta_degC'), (10, 'delta_degF')), (1000, 'delta_degC*delta_degF')),
660
661 (((100, 'delta_degF'), (10, 'kelvin')), (1000, 'delta_degF*kelvin')),
662 (((100, 'delta_degF'), (10, 'degC')), 'error'),
663 (((100, 'delta_degF'), (10, 'degF')), 'error'),
664 (((100, 'delta_degF'), (10, 'degR')), (1000, 'delta_degF*degR')),
665 (((100, 'delta_degF'), (10, 'delta_degC')), (1000, 'delta_degF*delta_degC')),
666 (((100, 'delta_degF'), (10, 'delta_degF')), (1000, 'delta_degF**2')),
667 ]
668
669 @ParameterizedTestCase.parameterize(("input", "expected_output"),
670 multiplications)
671 def test_multiplication(self, input_tuple, expected):
672 self.ureg.autoconvert_offset_to_baseunit = False
673 qin1, qin2 = input_tuple
674 q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
675 input_tuple = q1, q2
676 if expected == 'error':
677 self.assertRaises(OffsetUnitCalculusError, op.mul, q1, q2)
678 else:
679 expected = self.Q_(*expected)
680 self.assertEqual(op.mul(q1, q2).units, expected.units)
681 self.assertQuantityAlmostEqual(op.mul(q1, q2), expected,
682 atol=0.01)
683
684 @helpers.requires_numpy()
685 @ParameterizedTestCase.parameterize(("input", "expected_output"),
686 multiplications)
687 def test_inplace_multiplication(self, input_tuple, expected):
688 self.ureg.autoconvert_offset_to_baseunit = False
689 (q1v, q1u), (q2v, q2u) = input_tuple
690 # update input tuple with new values to have correct values on failure
691 input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
692 (np.array([q2v]*2, dtype=np.float), q2u))
693 Q_ = self.Q_
694 qin1, qin2 = input_tuple
695 q1, q2 = Q_(*qin1), Q_(*qin2)
696 q1_cp = copy.copy(q1)
697 if expected == 'error':
698 self.assertRaises(OffsetUnitCalculusError, op.imul, q1_cp, q2)
699 else:
700 expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
701 self.assertEqual(op.imul(q1_cp, q2).units, Q_(*expected).units)
702 q1_cp = copy.copy(q1)
703 self.assertQuantityAlmostEqual(op.imul(q1_cp, q2), Q_(*expected),
704 atol=0.01)
705
706 divisions = [
707 (((100, 'kelvin'), (10, 'kelvin')), (10, '')),
708 (((100, 'kelvin'), (10, 'degC')), 'error'),
709 (((100, 'kelvin'), (10, 'degF')), 'error'),
710 (((100, 'kelvin'), (10, 'degR')), (10, 'kelvin/degR')),
711 (((100, 'kelvin'), (10, 'delta_degC')), (10, 'kelvin/delta_degC')),
712 (((100, 'kelvin'), (10, 'delta_degF')), (10, 'kelvin/delta_degF')),
713
714 (((100, 'degC'), (10, 'kelvin')), 'error'),
715 (((100, 'degC'), (10, 'degC')), 'error'),
716 (((100, 'degC'), (10, 'degF')), 'error'),
717 (((100, 'degC'), (10, 'degR')), 'error'),
718 (((100, 'degC'), (10, 'delta_degC')), 'error'),
719 (((100, 'degC'), (10, 'delta_degF')), 'error'),
720
721 (((100, 'degF'), (10, 'kelvin')), 'error'),
722 (((100, 'degF'), (10, 'degC')), 'error'),
723 (((100, 'degF'), (10, 'degF')), 'error'),
724 (((100, 'degF'), (10, 'degR')), 'error'),
725 (((100, 'degF'), (10, 'delta_degC')), 'error'),
726 (((100, 'degF'), (10, 'delta_degF')), 'error'),
727
728 (((100, 'degR'), (10, 'kelvin')), (10, 'degR/kelvin')),
729 (((100, 'degR'), (10, 'degC')), 'error'),
730 (((100, 'degR'), (10, 'degF')), 'error'),
731 (((100, 'degR'), (10, 'degR')), (10, '')),
732 (((100, 'degR'), (10, 'delta_degC')), (10, 'degR/delta_degC')),
733 (((100, 'degR'), (10, 'delta_degF')), (10, 'degR/delta_degF')),
734
735 (((100, 'delta_degC'), (10, 'kelvin')), (10, 'delta_degC/kelvin')),
736 (((100, 'delta_degC'), (10, 'degC')), 'error'),
737 (((100, 'delta_degC'), (10, 'degF')), 'error'),
738 (((100, 'delta_degC'), (10, 'degR')), (10, 'delta_degC/degR')),
739 (((100, 'delta_degC'), (10, 'delta_degC')), (10, '')),
740 (((100, 'delta_degC'), (10, 'delta_degF')), (10, 'delta_degC/delta_degF')),
741
742 (((100, 'delta_degF'), (10, 'kelvin')), (10, 'delta_degF/kelvin')),
743 (((100, 'delta_degF'), (10, 'degC')), 'error'),
744 (((100, 'delta_degF'), (10, 'degF')), 'error'),
745 (((100, 'delta_degF'), (10, 'degR')), (10, 'delta_degF/degR')),
746 (((100, 'delta_degF'), (10, 'delta_degC')), (10, 'delta_degF/delta_degC')),
747 (((100, 'delta_degF'), (10, 'delta_degF')), (10, '')),
748 ]
749
750 @ParameterizedTestCase.parameterize(("input", "expected_output"),
751 divisions)
752 def test_truedivision(self, input_tuple, expected):
753 self.ureg.autoconvert_offset_to_baseunit = False
754 qin1, qin2 = input_tuple
755 q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
756 input_tuple = q1, q2
757 if expected == 'error':
758 self.assertRaises(OffsetUnitCalculusError, op.truediv, q1, q2)
759 else:
760 expected = self.Q_(*expected)
761 self.assertEqual(op.truediv(q1, q2).units, expected.units)
762 self.assertQuantityAlmostEqual(op.truediv(q1, q2), expected,
763 atol=0.01)
764
765 @helpers.requires_numpy()
766 @ParameterizedTestCase.parameterize(("input", "expected_output"),
767 divisions)
768 def test_inplace_truedivision(self, input_tuple, expected):
769 self.ureg.autoconvert_offset_to_baseunit = False
770 (q1v, q1u), (q2v, q2u) = input_tuple
771 # update input tuple with new values to have correct values on failure
772 input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
773 (np.array([q2v]*2, dtype=np.float), q2u))
774 Q_ = self.Q_
775 qin1, qin2 = input_tuple
776 q1, q2 = Q_(*qin1), Q_(*qin2)
777 q1_cp = copy.copy(q1)
778 if expected == 'error':
779 self.assertRaises(OffsetUnitCalculusError, op.itruediv, q1_cp, q2)
780 else:
781 expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
782 self.assertEqual(op.itruediv(q1_cp, q2).units, Q_(*expected).units)
783 q1_cp = copy.copy(q1)
784 self.assertQuantityAlmostEqual(op.itruediv(q1_cp, q2),
785 Q_(*expected), atol=0.01)
786
787 multiplications_with_autoconvert_to_baseunit = [
788 (((100, 'kelvin'), (10, 'degC')), (28315., 'kelvin**2')),
789 (((100, 'kelvin'), (10, 'degF')), (26092.78, 'kelvin**2')),
790
791 (((100, 'degC'), (10, 'kelvin')), (3731.5, 'kelvin**2')),
792 (((100, 'degC'), (10, 'degC')), (105657.42, 'kelvin**2')),
793 (((100, 'degC'), (10, 'degF')), (97365.20, 'kelvin**2')),
794 (((100, 'degC'), (10, 'degR')), (3731.5, 'kelvin*degR')),
795 (((100, 'degC'), (10, 'delta_degC')), (3731.5, 'kelvin*delta_degC')),
796 (((100, 'degC'), (10, 'delta_degF')), (3731.5, 'kelvin*delta_degF')),
797
798 (((100, 'degF'), (10, 'kelvin')), (3109.28, 'kelvin**2')),
799 (((100, 'degF'), (10, 'degC')), (88039.20, 'kelvin**2')),
800 (((100, 'degF'), (10, 'degF')), (81129.69, 'kelvin**2')),
801 (((100, 'degF'), (10, 'degR')), (3109.28, 'kelvin*degR')),
802 (((100, 'degF'), (10, 'delta_degC')), (3109.28, 'kelvin*delta_degC')),
803 (((100, 'degF'), (10, 'delta_degF')), (3109.28, 'kelvin*delta_degF')),
804
805 (((100, 'degR'), (10, 'degC')), (28315., 'degR*kelvin')),
806 (((100, 'degR'), (10, 'degF')), (26092.78, 'degR*kelvin')),
807
808 (((100, 'delta_degC'), (10, 'degC')), (28315., 'delta_degC*kelvin')),
809 (((100, 'delta_degC'), (10, 'degF')), (26092.78, 'delta_degC*kelvin')),
810
811 (((100, 'delta_degF'), (10, 'degC')), (28315., 'delta_degF*kelvin')),
812 (((100, 'delta_degF'), (10, 'degF')), (26092.78, 'delta_degF*kelvin')),
813 ]
814
815 @ParameterizedTestCase.parameterize(
816 ("input", "expected_output"),
817 multiplications_with_autoconvert_to_baseunit)
818 def test_multiplication_with_autoconvert(self, input_tuple, expected):
819 self.ureg.autoconvert_offset_to_baseunit = True
820 qin1, qin2 = input_tuple
821 q1, q2 = self.Q_(*qin1), self.Q_(*qin2)
822 input_tuple = q1, q2
823 if expected == 'error':
824 self.assertRaises(OffsetUnitCalculusError, op.mul, q1, q2)
825 else:
826 expected = self.Q_(*expected)
827 self.assertEqual(op.mul(q1, q2).units, expected.units)
828 self.assertQuantityAlmostEqual(op.mul(q1, q2), expected,
829 atol=0.01)
830
831 @helpers.requires_numpy()
832 @ParameterizedTestCase.parameterize(
833 ("input", "expected_output"),
834 multiplications_with_autoconvert_to_baseunit)
835 def test_inplace_multiplication_with_autoconvert(self, input_tuple, expected):
836 self.ureg.autoconvert_offset_to_baseunit = True
837 (q1v, q1u), (q2v, q2u) = input_tuple
838 # update input tuple with new values to have correct values on failure
839 input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u),
840 (np.array([q2v]*2, dtype=np.float), q2u))
841 Q_ = self.Q_
842 qin1, qin2 = input_tuple
843 q1, q2 = Q_(*qin1), Q_(*qin2)
844 q1_cp = copy.copy(q1)
845 if expected == 'error':
846 self.assertRaises(OffsetUnitCalculusError, op.imul, q1_cp, q2)
847 else:
848 expected = np.array([expected[0]]*2, dtype=np.float), expected[1]
849 self.assertEqual(op.imul(q1_cp, q2).units, Q_(*expected).units)
850 q1_cp = copy.copy(q1)
851 self.assertQuantityAlmostEqual(op.imul(q1_cp, q2), Q_(*expected),
852 atol=0.01)
853
854 multiplications_with_scalar = [
855 (((10, 'kelvin'), 2), (20., 'kelvin')),
856 (((10, 'kelvin**2'), 2), (20., 'kelvin**2')),
857 (((10, 'degC'), 2), (20., 'degC')),
858 (((10, '1/degC'), 2), 'error'),
859 (((10, 'degC**0.5'), 2), 'error'),
860 (((10, 'degC**2'), 2), 'error'),
861 (((10, 'degC**-2'), 2), 'error'),
862 ]
863
864 @ParameterizedTestCase.parameterize(
865 ("input", "expected_output"), multiplications_with_scalar)
866 def test_multiplication_with_scalar(self, input_tuple, expected):
867 self.ureg.default_as_delta = False
868 in1, in2 = input_tuple
869 if type(in1) is tuple:
870 in1, in2 = self.Q_(*in1), in2
871 else:
872 in1, in2 = in1, self.Q_(*in2)
873 input_tuple = in1, in2 # update input_tuple for better tracebacks
874 if expected == 'error':
875 self.assertRaises(OffsetUnitCalculusError, op.mul, in1, in2)
876 else:
877 expected = self.Q_(*expected)
878 self.assertEqual(op.mul(in1, in2).units, expected.units)
879 self.assertQuantityAlmostEqual(op.mul(in1, in2), expected,
880 atol=0.01)
881
882 divisions_with_scalar = [ # without / with autoconvert to base unit
883 (((10, 'kelvin'), 2), [(5., 'kelvin'), (5., 'kelvin')]),
884 (((10, 'kelvin**2'), 2), [(5., 'kelvin**2'), (5., 'kelvin**2')]),
885 (((10, 'degC'), 2), ['error', 'error']),
886 (((10, 'degC**2'), 2), ['error', 'error']),
887 (((10, 'degC**-2'), 2), ['error', 'error']),
888
889 ((2, (10, 'kelvin')), [(0.2, '1/kelvin'), (0.2, '1/kelvin')]),
890 ((2, (10, 'degC')), ['error', (2/283.15, '1/kelvin')]),
891 ((2, (10, 'degC**2')), ['error', 'error']),
892 ((2, (10, 'degC**-2')), ['error', 'error']),
893 ]
894
895 @ParameterizedTestCase.parameterize(
896 ("input", "expected_output"), divisions_with_scalar)
897 def test_division_with_scalar(self, input_tuple, expected):
898 self.ureg.default_as_delta = False
899 in1, in2 = input_tuple
900 if type(in1) is tuple:
901 in1, in2 = self.Q_(*in1), in2
902 else:
903 in1, in2 = in1, self.Q_(*in2)
904 input_tuple = in1, in2 # update input_tuple for better tracebacks
905 expected_copy = expected[:]
906 for i, mode in enumerate([False, True]):
907 self.ureg.autoconvert_offset_to_baseunit = mode
908 if expected_copy[i] == 'error':
909 self.assertRaises(OffsetUnitCalculusError, op.truediv, in1, in2)
910 else:
911 expected = self.Q_(*expected_copy[i])
912 self.assertEqual(op.truediv(in1, in2).units, expected.units)
913 self.assertQuantityAlmostEqual(op.truediv(in1, in2), expected)
914
915 exponentiation = [ # resuls without / with autoconvert
916 (((10, 'degC'), 1), [(10, 'degC'), (10, 'degC')]),
917 (((10, 'degC'), 0.5), ['error', (283.15**0.5, 'kelvin**0.5')]),
918 (((10, 'degC'), 0), [(1., ''), (1., '')]),
919 (((10, 'degC'), -1), ['error', (1/(10+273.15), 'kelvin**-1')]),
920 (((10, 'degC'), -2), ['error', (1/(10+273.15)**2., 'kelvin**-2')]),
921 ((( 0, 'degC'), -2), ['error', (1/(273.15)**2, 'kelvin**-2')]),
922 (((10, 'degC'), (2, '')), ['error', ((283.15)**2, 'kelvin**2')]),
923 (((10, 'degC'), (10, 'degK')), ['error', 'error']),
924
925 (((10, 'kelvin'), (2, '')), [(100., 'kelvin**2'), (100., 'kelvin**2')]),
926
927 (( 2, (2, 'kelvin')), ['error', 'error']),
928 (( 2, (500., 'millikelvin/kelvin')), [2**0.5, 2**0.5]),
929 (( 2, (0.5, 'kelvin/kelvin')), [2**0.5, 2**0.5]),
930 (((10, 'degC'), (500., 'millikelvin/kelvin')),
931 ['error', (283.15**0.5, 'kelvin**0.5')]),
932 ]
933
934 @ParameterizedTestCase.parameterize(
935 ("input", "expected_output"), exponentiation)
936 def test_exponentiation(self, input_tuple, expected):
937 self.ureg.default_as_delta = False
938 in1, in2 = input_tuple
939 if type(in1) is tuple and type(in2) is tuple:
940 in1, in2 = self.Q_(*in1), self.Q_(*in2)
941 elif not type(in1) is tuple and type(in2) is tuple:
942 in2 = self.Q_(*in2)
943 else:
944 in1 = self.Q_(*in1)
945 input_tuple = in1, in2
946 expected_copy = expected[:]
947 for i, mode in enumerate([False, True]):
948 self.ureg.autoconvert_offset_to_baseunit = mode
949 if expected_copy[i] == 'error':
950 self.assertRaises((OffsetUnitCalculusError,
951 DimensionalityError), op.pow, in1, in2)
952 else:
953 if type(expected_copy[i]) is tuple:
954 expected = self.Q_(*expected_copy[i])
955 self.assertEqual(op.pow(in1, in2).units, expected.units)
956 else:
957 expected = expected_copy[i]
958 self.assertQuantityAlmostEqual(op.pow(in1, in2), expected)
959
960 @helpers.requires_numpy()
961 @ParameterizedTestCase.parameterize(
962 ("input", "expected_output"), exponentiation)
963 def test_inplace_exponentiation(self, input_tuple, expected):
964 self.ureg.default_as_delta = False
965 in1, in2 = input_tuple
966 if type(in1) is tuple and type(in2) is tuple:
967 (q1v, q1u), (q2v, q2u) = in1, in2
968 in1 = self.Q_(*(np.array([q1v]*2, dtype=np.float), q1u))
969 in2 = self.Q_(q2v, q2u)
970 elif not type(in1) is tuple and type(in2) is tuple:
971 in2 = self.Q_(*in2)
972 else:
973 in1 = self.Q_(*in1)
974
975 input_tuple = in1, in2
976
977 expected_copy = expected[:]
978 for i, mode in enumerate([False, True]):
979 self.ureg.autoconvert_offset_to_baseunit = mode
980 in1_cp = copy.copy(in1)
981 if expected_copy[i] == 'error':
982 self.assertRaises((OffsetUnitCalculusError,
983 DimensionalityError), op.ipow, in1_cp, in2)
984 else:
985 if type(expected_copy[i]) is tuple:
986 expected = self.Q_(np.array([expected_copy[i][0]]*2,
987 dtype=np.float),
988 expected_copy[i][1])
989 self.assertEqual(op.ipow(in1_cp, in2).units, expected.units)
990 else:
991 expected = np.array([expected_copy[i]]*2, dtype=np.float)
992
993
994 in1_cp = copy.copy(in1)
995 self.assertQuantityAlmostEqual(op.ipow(in1_cp, in2), expected)
453996
=== modified file 'pint/testsuite/test_umath.py'
--- pint/testsuite/test_umath.py 2014-09-05 23:26:05 +0000
+++ pint/testsuite/test_umath.py 2015-01-07 15:51:38 +0000
@@ -52,17 +52,29 @@
52 except Exception as e:52 except Exception as e:
53 self.assertFalse(True, msg='{0} not raised but {1}\n{2}'.format(ExcType, e, msg))53 self.assertFalse(True, msg='{0} not raised but {1}\n{2}'.format(ExcType, e, msg))
5454
55 def _testn(self, func, ok_with, raise_with=(), results=None):
56 self._test1(func, ok_with, raise_with, output_units=None, results=results)
57
58 def _test1(self, func, ok_with, raise_with=(), output_units='same', results=None, rtol=1e-6):55 def _test1(self, func, ok_with, raise_with=(), output_units='same', results=None, rtol=1e-6):
56 """Test function that takes a single argument and returns Quantity.
57
58 :param func: function callable.
59 :param ok_with: iterables of values that work fine.
60 :param raise_with: iterables of values that raise exceptions.
61 :param output_units: units to be used when building results.
62 'same': ok_with[n].units (default).
63 is float: ok_with[n].units ** output_units.
64 None: no output units, the result should be an ndarray.
65 Other value will be parsed as unit.
66 :param results: iterable of results.
67 If None, the result will be obtained by applying
68 func to each ok_with value
69 :param rtol: relative tolerance.
70 """
59 if results is None:71 if results is None:
60 results = [None, ] * len(ok_with)72 results = [None, ] * len(ok_with)
61 for x1, res in zip(ok_with, results):73 for x1, res in zip(ok_with, results):
62 err_msg = 'At {0} with {1}'.format(func.__name__, x1)74 err_msg = 'At {0} with {1}'.format(func.__name__, x1)
63 if output_units == 'same':75 if output_units == 'same':
64 ou = x1.units76 ou = x1.units
65 elif isinstance(output_units, int):77 elif isinstance(output_units, (int, float)):
66 ou = x1.units ** output_units78 ou = x1.units ** output_units
67 else:79 else:
68 ou = output_units80 ou = output_units
@@ -70,20 +82,45 @@
70 qm = func(x1)82 qm = func(x1)
71 if res is None:83 if res is None:
72 res = func(x1.magnitude)84 res = func(x1.magnitude)
73 if ou:85 if ou is not None:
74 res = self.Q_(res, ou)86 res = self.Q_(res, ou)
75 if isinstance(res, self.Q_):87
76 self.assertIsInstance(qm, self.Q_, msg=err_msg + ' {0!r} is not Quantity'.format(qm))88 self.assertQuantityAlmostEqual(qm, res, rtol=rtol, msg=err_msg)
77 qm = qm.magnitude
78 res = res.magnitude
79 np.testing.assert_allclose(qm, res, rtol=rtol, err_msg=err_msg)
8089
81 for x1 in raise_with:90 for x1 in raise_with:
82 self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1),91 self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1),
83 ValueError, func, x1)92 ValueError, func, x1)
8493
94 def _testn(self, func, ok_with, raise_with=(), results=None):
95 """Test function that takes a single argument and returns and ndarray (not a Quantity)
96
97 :param func: function callable.
98 :param ok_with: iterables of values that work fine.
99 :param raise_with: iterables of values that raise exceptions.
100 :param results: iterable of results.
101 If None, the result will be obtained by applying
102 func to each ok_with value
103 """
104 self._test1(func, ok_with, raise_with, output_units=None, results=results)
105
85 def _test1_2o(self, func, ok_with, raise_with=(), output_units=('same', 'same'),106 def _test1_2o(self, func, ok_with, raise_with=(), output_units=('same', 'same'),
86 results=None, rtol=1e-6):107 results=None, rtol=1e-6):
108 """Test functions that takes a single argument and return two Quantities.
109
110 :param func: function callable.
111 :param ok_with: iterables of values that work fine.
112 :param raise_with: iterables of values that raise exceptions.
113 :param output_units: tuple of units to be used when building the result tuple.
114 'same': ok_with[n].units (default).
115 is float: ok_with[n].units ** output_units.
116 None: no output units, the result should be an ndarray.
117 Other value will be parsed as unit.
118 :param results: iterable of results.
119 If None, the result will be obtained by applying
120 func to each ok_with value
121 :param rtol: relative tolerance.
122 """
123
87 if results is None:124 if results is None:
88 results = [None, ] * len(ok_with)125 results = [None, ] * len(ok_with)
89 for x1, res in zip(ok_with, results):126 for x1, res in zip(ok_with, results):
@@ -95,26 +132,35 @@
95 for ndx, (qm, re, ou) in enumerate(zip(qms, res, output_units)):132 for ndx, (qm, re, ou) in enumerate(zip(qms, res, output_units)):
96 if ou == 'same':133 if ou == 'same':
97 ou = x1.units134 ou = x1.units
98 elif isinstance(ou, int):135 elif isinstance(ou, (int, float)):
99 ou = x1.units ** ou136 ou = x1.units ** ou
100137
101 if ou is not None:138 if ou is not None:
102 re = self.Q_(re, ou)139 re = self.Q_(re, ou)
103140
104 if isinstance(re, self.Q_):141 self.assertQuantityAlmostEqual(qm, re, rtol=rtol, msg=err_msg)
105 self.assertIsInstance(qm, self.Q_, msg=err_msg)
106 qm = qm.magnitude
107 re = re.magnitude
108 np.testing.assert_allclose(qm, re, rtol=rtol, err_msg=err_msg)
109142
110 for x1 in raise_with:143 for x1 in raise_with:
111 self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1),144 self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1),
112 ValueError, func, x1)145 ValueError, func, x1)
113146
114 def _testn2(self, func, x1, ok_with, raise_with=()):
115 self._test2(func, x1, ok_with, raise_with, output_units=None)
116
117 def _test2(self, func, x1, ok_with, raise_with=(), output_units='same', rtol=1e-6, convert2=True):147 def _test2(self, func, x1, ok_with, raise_with=(), output_units='same', rtol=1e-6, convert2=True):
148 """Test function that takes two arguments and return a Quantity.
149
150 :param func: function callable.
151 :param x1: first argument of func.
152 :param ok_with: iterables of values that work fine.
153 :param raise_with: iterables of values that raise exceptions.
154 :param output_units: units to be used when building results.
155 'same': x1.units (default).
156 'prod': x1.units * ok_with[n].units
157 'div': x1.units / ok_with[n].units
158 'second': x1.units * ok_with[n]
159 None: no output units, the result should be an ndarray.
160 Other value will be parsed as unit.
161 :param rtol: relative tolerance.
162 :param convert2: if the ok_with[n] should be converted to x1.units.
163 """
118 for x2 in ok_with:164 for x2 in ok_with:
119 err_msg = 'At {0} with {1} and {2}'.format(func.__name__, x1, x2)165 err_msg = 'At {0} with {1} and {2}'.format(func.__name__, x1, x2)
120 if output_units == 'same':166 if output_units == 'same':
@@ -136,18 +182,25 @@
136 m2 = getattr(x2, 'magnitude', x2)182 m2 = getattr(x2, 'magnitude', x2)
137183
138 res = func(x1.magnitude, m2)184 res = func(x1.magnitude, m2)
139 if ou:185 if ou is not None:
140 res = self.Q_(res, ou)186 res = self.Q_(res, ou)
141 if isinstance(res, self.Q_):187
142 self.assertIsInstance(qm, self.Q_, msg=err_msg)188 self.assertQuantityAlmostEqual(qm, res, rtol=rtol, msg=err_msg)
143 qm = qm.magnitude
144 res = res.magnitude
145 np.testing.assert_allclose(qm, res, rtol=rtol, err_msg=err_msg)
146189
147 for x2 in raise_with:190 for x2 in raise_with:
148 self.assertRaisesMsg('At {0} with {1} and {2}'.format(func.__name__, x1, x2),191 self.assertRaisesMsg('At {0} with {1} and {2}'.format(func.__name__, x1, x2),
149 ValueError, func, x1, x2)192 ValueError, func, x1, x2)
150193
194 def _testn2(self, func, x1, ok_with, raise_with=()):
195 """Test function that takes two arguments and return a ndarray.
196
197 :param func: function callable.
198 :param x1: first argument of func.
199 :param ok_with: iterables of values that work fine.
200 :param raise_with: iterables of values that raise exceptions.
201 """
202 self._test2(func, x1, ok_with, raise_with, output_units=None)
203
151204
152@helpers.requires_numpy()205@helpers.requires_numpy()
153class TestMathUfuncs(TestUFuncs):206class TestMathUfuncs(TestUFuncs):
@@ -248,7 +301,7 @@
248 self.q1,301 self.q1,
249 (self.q2, self.qs, self.qless),302 (self.q2, self.qs, self.qless),
250 (),303 (),
251 'div', convert2=False)304 'same', convert2=False)
252305
253 def test_mod(self):306 def test_mod(self):
254 self._test2(np.mod,307 self._test2(np.mod,
@@ -322,7 +375,7 @@
322 self._test1(np.sqrt,375 self._test1(np.sqrt,
323 (self.q2, self.qs, self.qless, self.qi),376 (self.q2, self.qs, self.qless, self.qi),
324 (),377 (),
325 'same')378 0.5)
326379
327 def test_square(self):380 def test_square(self):
328 self._test1(np.square,381 self._test1(np.square,
@@ -334,7 +387,7 @@
334 self._test1(np.reciprocal,387 self._test1(np.reciprocal,
335 (self.q2, self.qs, self.qless, self.qi),388 (self.q2, self.qs, self.qless, self.qi),
336 (),389 (),
337 2)390 -1)
338391
339392
340@helpers.requires_numpy()393@helpers.requires_numpy()
@@ -469,10 +522,16 @@
469 ), (self.ureg.m, ), 'radians')522 ), (self.ureg.m, ), 'radians')
470523
471 def test_rad2deg(self):524 def test_rad2deg(self):
472 self._test1(np.rad2deg, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless,525 self._test1(np.rad2deg,
473 np.arange(0, pi/2, pi/4) * self.ureg.radian,526 (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless,
474 np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m527 np.arange(0, pi/2, pi/4) * self.ureg.radian,
475 ), (self.ureg.m, ), 'degree', results=(None, None, np.rad2deg(np.arange(0, pi/2, pi/4)*0.001)))528 np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m,
529 ),
530 (self.ureg.m, ), 'degree',
531 results=(None,
532 None,
533 np.rad2deg(np.arange(0, pi/2, pi/4)*0.001) * self.ureg.degree,
534 ))
476535
477536
478537
479538
=== modified file 'pint/testsuite/test_unit.py'
--- pint/testsuite/test_unit.py 2014-09-05 23:26:05 +0000
+++ pint/testsuite/test_unit.py 2015-01-07 15:51:38 +0000
@@ -12,8 +12,9 @@
12 DimensionDefinition, _freeze, Converter, UnitRegistry,12 DimensionDefinition, _freeze, Converter, UnitRegistry,
13 LazyRegistry, ParserHelper)13 LazyRegistry, ParserHelper)
14from pint import DimensionalityError, UndefinedUnitError14from pint import DimensionalityError, UndefinedUnitError
15from pint.compat import u, unittest, np15from pint.compat import u, unittest, np, string_types
16from pint.testsuite import QuantityTestCase, helpers, BaseTestCase16from pint.testsuite import QuantityTestCase, helpers, BaseTestCase
17from pint.testsuite.parameterized import ParameterizedTestCase
1718
1819
19class TestConverter(BaseTestCase):20class TestConverter(BaseTestCase):
@@ -231,6 +232,9 @@
231232
232 FORCE_NDARRAY = False233 FORCE_NDARRAY = False
233234
235 def setup(self):
236 self.ureg.autoconvert_offset_to_baseunit = False
237
234 def test_base(self):238 def test_base(self):
235 ureg = UnitRegistry(None)239 ureg = UnitRegistry(None)
236 ureg.define('meter = [length]')240 ureg.define('meter = [length]')
@@ -324,16 +328,16 @@
324 q = self.Q_(1, 'g/(m**2*s)')328 q = self.Q_(1, 'g/(m**2*s)')
325 self.assertEqual(self.Q_(q.magnitude, str(q.units)), q)329 self.assertEqual(self.Q_(q.magnitude, str(q.units)), q)
326330
327 def test_to_delta(self):331 def test_as_delta(self):
328 parse = self.ureg.parse_units332 parse = self.ureg.parse_units
329 self.assertEqual(parse('kelvin', to_delta=True), UnitsContainer(kelvin=1))333 self.assertEqual(parse('kelvin', as_delta=True), UnitsContainer(kelvin=1))
330 self.assertEqual(parse('kelvin', to_delta=False), UnitsContainer(kelvin=1))334 self.assertEqual(parse('kelvin', as_delta=False), UnitsContainer(kelvin=1))
331 self.assertEqual(parse('kelvin**(-1)', to_delta=True), UnitsContainer(kelvin=-1))335 self.assertEqual(parse('kelvin**(-1)', as_delta=True), UnitsContainer(kelvin=-1))
332 self.assertEqual(parse('kelvin**(-1)', to_delta=False), UnitsContainer(kelvin=-1))336 self.assertEqual(parse('kelvin**(-1)', as_delta=False), UnitsContainer(kelvin=-1))
333 self.assertEqual(parse('kelvin**2', to_delta=True), UnitsContainer(kelvin=2))337 self.assertEqual(parse('kelvin**2', as_delta=True), UnitsContainer(kelvin=2))
334 self.assertEqual(parse('kelvin**2', to_delta=False), UnitsContainer(kelvin=2))338 self.assertEqual(parse('kelvin**2', as_delta=False), UnitsContainer(kelvin=2))
335 self.assertEqual(parse('kelvin*meter', to_delta=True), UnitsContainer(kelvin=1, meter= 1))339 self.assertEqual(parse('kelvin*meter', as_delta=True), UnitsContainer(kelvin=1, meter= 1))
336 self.assertEqual(parse('kelvin*meter', to_delta=False), UnitsContainer(kelvin=1, meter=1))340 self.assertEqual(parse('kelvin*meter', as_delta=False), UnitsContainer(kelvin=1, meter=1))
337341
338 def test_name(self):342 def test_name(self):
339 self.assertRaises(UndefinedUnitError, self.ureg.get_name, 'asdf')343 self.assertRaises(UndefinedUnitError, self.ureg.get_name, 'asdf')
@@ -357,18 +361,6 @@
357 self.assertEqual(self.ureg.get_symbol('international_foot'), 'ft')361 self.assertEqual(self.ureg.get_symbol('international_foot'), 'ft')
358 self.assertEqual(self.ureg.get_symbol('international_inch'), 'in')362 self.assertEqual(self.ureg.get_symbol('international_inch'), 'in')
359363
360 @unittest.expectedFailure
361 def test_delta_in_diff(self):
362 """This might be supported in future versions
363 """
364 xk = 1 * self.ureg.kelvin
365 yk = 2 * self.ureg.kelvin
366 yf = yk.to('degF')
367 yc = yk.to('degC')
368 self.assertEqual(yk - xk, 1 * self.ureg.kelvin)
369 self.assertEqual(yf - xk, 1 * self.ureg.kelvin)
370 self.assertEqual(yc - xk, 1 * self.ureg.kelvin)
371
372 def test_pint(self):364 def test_pint(self):
373 p = self.ureg.pint365 p = self.ureg.pint
374 l = self.ureg.liter366 l = self.ureg.liter
@@ -440,12 +432,13 @@
440 self.assertEqual(h2(3, 1), (3 * ureg.meter, 1 * ureg.cm))432 self.assertEqual(h2(3, 1), (3 * ureg.meter, 1 * ureg.cm))
441433
442 def test_to_ref_vs_to(self):434 def test_to_ref_vs_to(self):
435 self.ureg.autoconvert_offset_to_baseunit = True
443 q = 8. * self.ureg.inch436 q = 8. * self.ureg.inch
444 t = 8. * self.ureg.degF437 t = 8. * self.ureg.degF
445 dt = 8. * self.ureg.delta_degF438 dt = 8. * self.ureg.delta_degF
446 self.assertEqual(q.to('cm').magnitude, self.ureg._units['inch'].converter.to_reference(8.))439 self.assertEqual(q.to('cm').magnitude, self.ureg._units['inch'].converter.to_reference(8.))
447 self.assertEqual(t.to('kelvin').magnitude, self.ureg._units['degF'].converter.to_reference(8.))440 self.assertEqual(t.to('kelvin').magnitude, self.ureg._units['degF'].converter.to_reference(8.))
448 self.assertEqual(dt.to('delta_kelvin').magnitude, self.ureg._units['delta_degF'].converter.to_reference(8.))441 self.assertEqual(dt.to('kelvin').magnitude, self.ureg._units['delta_degF'].converter.to_reference(8.))
449442
450 def test_redefinition(self):443 def test_redefinition(self):
451 d = UnitRegistry().define444 d = UnitRegistry().define
@@ -473,7 +466,7 @@
473 # Conversions with single units take a different codepath than466 # Conversions with single units take a different codepath than
474 # Conversions with more than one unit.467 # Conversions with more than one unit.
475 src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1)468 src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1)
476 src_dst2 = UnitsContainer(meter=1, seconds=-1), UnitsContainer(inch=1, minutes=-1)469 src_dst2 = UnitsContainer(meter=1, second=-1), UnitsContainer(inch=1, minute=-1)
477 for src, dst in (src_dst1, src_dst2):470 for src, dst in (src_dst1, src_dst2):
478 v = ureg.convert(1, src, dst),471 v = ureg.convert(1, src, dst),
479472
@@ -598,3 +591,52 @@
598 msg = "Cannot convert from 'a' (c) to 'b' (d)msg"591 msg = "Cannot convert from 'a' (c) to 'b' (d)msg"
599 ex = DimensionalityError('a', 'b', 'c', 'd', 'msg')592 ex = DimensionalityError('a', 'b', 'c', 'd', 'msg')
600 self.assertEqual(str(ex), msg)593 self.assertEqual(str(ex), msg)
594
595
596class TestConvertWithOffset(QuantityTestCase, ParameterizedTestCase):
597
598 # The dicts in convert_with_offset are used to create a UnitsContainer.
599 # We create UnitsContainer to avoid any auto-conversion of units.
600 convert_with_offset = [
601 (({'degC': 1}, {'degC': 1}), 10),
602 (({'degC': 1}, {'kelvin': 1}), 283.15),
603 (({'degC': 1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'),
604 (({'degC': 1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 283150),
605
606 (({'kelvin': 1}, {'degC': 1}), -263.15),
607 (({'kelvin': 1}, {'kelvin': 1}), 10),
608 (({'kelvin': 1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'),
609 (({'kelvin': 1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 10000),
610
611 (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1}), 'error'),
612 (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1}), 'error'),
613 (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 10),
614 (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 'error'),
615
616 (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1}), -273.14),
617 (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1}), 0.01),
618 (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'),
619 (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 10),
620
621 (({'degC': 2}, {'kelvin': 2}), 'error'),
622 (({'degC': 1, 'degF': 1}, {'kelvin': 2}), 'error'),
623 (({'degC': 1, 'kelvin': 1}, {'kelvin': 2}), 'error'),
624 ]
625
626 @ParameterizedTestCase.parameterize(("input", "expected_output"),
627 convert_with_offset)
628 def test_to_and_from_offset_units(self, input_tuple, expected):
629 src, dst = input_tuple
630 src, dst = UnitsContainer(src), UnitsContainer(dst)
631 value = 10.
632 convert = self.ureg.convert
633 if isinstance(expected, string_types):
634 self.assertRaises(DimensionalityError, convert, value, src, dst)
635 if src != dst:
636 self.assertRaises(DimensionalityError, convert, value, dst, src)
637 else:
638 self.assertQuantityAlmostEqual(convert(value, src, dst),
639 expected, atol=0.001)
640 if src != dst:
641 self.assertQuantityAlmostEqual(convert(expected, dst, src),
642 value, atol=0.001)
601643
=== modified file 'pint/unit.py'
--- pint/unit.py 2014-09-05 23:26:05 +0000
+++ pint/unit.py 2015-01-07 15:51:38 +0000
@@ -87,7 +87,7 @@
87 """Raised when trying to convert between incompatible units.87 """Raised when trying to convert between incompatible units.
88 """88 """
8989
90 def __init__(self, units1, units2, dim1=None, dim2=None, extra_msg=None):90 def __init__(self, units1, units2, dim1=None, dim2=None, extra_msg=''):
91 super(DimensionalityError, self).__init__()91 super(DimensionalityError, self).__init__()
92 self.units1 = units192 self.units1 = units1
93 self.units2 = units293 self.units2 = units2
@@ -103,12 +103,25 @@
103 dim1 = ''103 dim1 = ''
104 dim2 = ''104 dim2 = ''
105105
106 msg = "Cannot convert from '{0}'{1} to '{2}'{3}".format(self.units1, dim1, self.units2, dim2)106 msg = "Cannot convert from '{0}'{1} to '{2}'{3}" + self.extra_msg
107107
108 if self.extra_msg:108 return msg.format(self.units1, dim1, self.units2, dim2)
109 return msg + self.extra_msg109
110110
111 return msg111class OffsetUnitCalculusError(ValueError):
112 """Raised on ambiguous operations with offset units.
113 """
114 def __init__(self, units1, units2='', extra_msg=''):
115 super(ValueError, self).__init__()
116 self.units1 = units1
117 self.units2 = units2
118 self.extra_msg = extra_msg
119
120 def __str__(self):
121 msg = ("Ambiguous operation with offset unit (%s)." %
122 ', '.join(['%s' % u for u in [self.units1, self.units2] if u])
123 + self.extra_msg)
124 return msg.format(self.units1, self.units2)
112125
113126
114class Converter(object):127class Converter(object):
@@ -244,7 +257,7 @@
244257
245258
246def _is_dim(name):259def _is_dim(name):
247 return name.startswith('[') and name.endswith(']')260 return name[0] == '[' and name[-1] == ']'
248261
249262
250class PrefixDefinition(Definition):263class PrefixDefinition(Definition):
@@ -289,7 +302,7 @@
289 'Base units must be referenced only to dimensions. '302 'Base units must be referenced only to dimensions. '
290 'Derived units must be referenced only to units.')303 'Derived units must be referenced only to units.')
291 self.reference = UnitsContainer(converter.items())304 self.reference = UnitsContainer(converter.items())
292 if 'offset' in modifiers:305 if modifiers.get('offset', 0.) != 0.:
293 converter = OffsetConverter(converter.scale, modifiers['offset'])306 converter = OffsetConverter(converter.scale, modifiers['offset'])
294 else:307 else:
295 converter = ScaleConverter(converter.scale)308 converter = ScaleConverter(converter.scale)
@@ -445,14 +458,19 @@
445 Empty to load the default definition file.458 Empty to load the default definition file.
446 None to leave the UnitRegistry empty.459 None to leave the UnitRegistry empty.
447 :param force_ndarray: convert any input, scalar or not to a numpy.ndarray.460 :param force_ndarray: convert any input, scalar or not to a numpy.ndarray.
448 :param default_to_delta: In the context of a multiplication of units, interpret461 :param default_as_delta: In the context of a multiplication of units, interpret
449 non-multiplicative units as their *delta* counterparts.462 non-multiplicative units as their *delta* counterparts.
463 :autoconvert_offset_to_baseunit: If True converts offset units in quantites are
464 converted to their base units in multiplicative
465 context. If False no conversion happens.
450 :param on_redefinition: action to take in case a unit is redefined.466 :param on_redefinition: action to take in case a unit is redefined.
451 'warn', 'raise', 'ignore'467 'warn', 'raise', 'ignore'
452 :type on_redefintion: str468 :type on_redefintion: str
453 """469 """
454470
455 def __init__(self, filename='', force_ndarray=False, default_to_delta=True, on_redefinition='warn'):471 def __init__(self, filename='', force_ndarray=False, default_as_delta=True,
472 autoconvert_offset_to_baseunit=False,
473 on_redefinition='warn'):
456 self.Quantity = build_quantity_class(self, force_ndarray)474 self.Quantity = build_quantity_class(self, force_ndarray)
457 self.Measurement = build_measurement_class(self, force_ndarray)475 self.Measurement = build_measurement_class(self, force_ndarray)
458476
@@ -491,9 +509,16 @@
491 #: Maps dimensionality (_freeze(UnitsContainer)) to Units (_freeze(UnitsContainer))509 #: Maps dimensionality (_freeze(UnitsContainer)) to Units (_freeze(UnitsContainer))
492 self._dimensionality_cache = TransformDict(_freeze)510 self._dimensionality_cache = TransformDict(_freeze)
493511
512 #: Cache the unit name associated to user input. ('mV' -> 'millivolt')
513 self._parse_unit_cache = dict()
514
494 #: When performing a multiplication of units, interpret515 #: When performing a multiplication of units, interpret
495 #: non-multiplicative units as their *delta* counterparts.516 #: non-multiplicative units as their *delta* counterparts.
496 self.default_to_delta = default_to_delta517 self.default_as_delta = default_as_delta
518
519 # Determines if quantities with offset units are converted to their
520 # base units on multiplication and division.
521 self.autoconvert_offset_to_baseunit = autoconvert_offset_to_baseunit
497522
498 if filename == '':523 if filename == '':
499 self.load_definitions('default_en.txt', True)524 self.load_definitions('default_en.txt', True)
@@ -504,6 +529,9 @@
504529
505 self._build_cache()530 self._build_cache()
506531
532 def __name__(self):
533 return 'UnitRegistry'
534
507 def __getattr__(self, item):535 def __getattr__(self, item):
508 return self.Quantity(1, item)536 return self.Quantity(1, item)
509537
@@ -697,7 +725,8 @@
697725
698 _adder(alias, definition)726 _adder(alias, definition)
699727
700 if isinstance(definition.converter, OffsetConverter):728 # define additional "delta_" units for units with an offset
729 if getattr(definition.converter, "offset", 0.0) != 0.0:
701 d_name = 'delta_' + definition.name730 d_name = 'delta_' + definition.name
702 if definition.symbol:731 if definition.symbol:
703 d_symbol = 'Δ' + definition.symbol732 d_symbol = 'Δ' + definition.symbol
@@ -710,7 +739,7 @@
710 return '[delta_' + _name[1:]739 return '[delta_' + _name[1:]
711 return 'delta_' + _name740 return 'delta_' + _name
712741
713 d_reference = UnitsContainer(dict((prep(ref), value)742 d_reference = UnitsContainer(dict((ref, value)
714 for ref, value in definition.reference.items()))743 for ref, value in definition.reference.items()))
715744
716 self.define(UnitDefinition(d_name, d_symbol, d_aliases,745 self.define(UnitDefinition(d_name, d_symbol, d_aliases,
@@ -818,7 +847,7 @@
818 return ''847 return ''
819848
820 try:849 try:
821 return self._units[name_or_alias].name850 return self._units[name_or_alias]._name
822 except KeyError:851 except KeyError:
823 pass852 pass
824853
@@ -857,15 +886,17 @@
857886
858 return self._prefixes[prefix].symbol + self._units[unit_name].symbol887 return self._prefixes[prefix].symbol + self._units[unit_name].symbol
859888
889 def _get_symbol(self, name):
890 return self._units[name].symbol
891
860 def get_dimensionality(self, input_units):892 def get_dimensionality(self, input_units):
861 """Convert unit or dict of units or dimensions to a dict of base dimensions893 """Convert unit or dict of units or dimensions to a dict of base dimensions
862894
863 :param input_units:895 :param input_units:
864 :return: dimensionality896 :return: dimensionality
865 """897 """
866 dims = UnitsContainer()
867 if not input_units:898 if not input_units:
868 return dims899 return UnitsContainer()
869900
870 if isinstance(input_units, string_types):901 if isinstance(input_units, string_types):
871 input_units = ParserHelper.from_string(input_units)902 input_units = ParserHelper.from_string(input_units)
@@ -873,24 +904,31 @@
873 if input_units in self._dimensionality_cache:904 if input_units in self._dimensionality_cache:
874 return copy.copy(self._dimensionality_cache[input_units])905 return copy.copy(self._dimensionality_cache[input_units])
875906
876 for key, value in input_units.items():907 accumulator = defaultdict(float)
908 self._get_dimensionality_recurse(input_units, 1.0, accumulator)
909
910 dims = UnitsContainer(dict((k, v) for k, v in accumulator.items() if v != 0.))
911
912 if '[]' in dims:
913 del dims['[]']
914
915 self._dimensionality_cache[input_units] = copy.copy(dims)
916
917 return dims
918
919 def _get_dimensionality_recurse(self, ref, exp, accumulator):
920 for key in ref:
921 exp2 = exp*ref[key]
877 if _is_dim(key):922 if _is_dim(key):
878 reg = self._dimensions[key]923 reg = self._dimensions[key]
879 if reg.is_base:924 if reg.is_base:
880 dims.add(key, value)925 accumulator[key] += exp2
881 else:926 elif reg.reference is not None:
882 dims *= self.get_dimensionality(reg.reference) ** value927 self._get_dimensionality_recurse(reg.reference, exp2, accumulator)
883 else:928 else:
884 reg = self._units[self.get_name(key)]929 reg = self._units[self.get_name(key)]
885 if reg.is_base:930 if reg.reference is not None:
886 dims *= reg.reference ** value931 self._get_dimensionality_recurse(reg.reference, exp2, accumulator)
887 else:
888 dims *= self.get_dimensionality(reg.reference) ** value
889
890 if '[]' in dims:
891 del dims['[]']
892
893 return dims
894932
895 def get_base_units(self, input_units, check_nonmult=True):933 def get_base_units(self, input_units, check_nonmult=True):
896 """Convert unit or dict of units to the base units.934 """Convert unit or dict of units to the base units.
@@ -914,27 +952,32 @@
914 if check_nonmult and input_units in self._base_units_cache:952 if check_nonmult and input_units in self._base_units_cache:
915 return copy.deepcopy(self._base_units_cache[input_units])953 return copy.deepcopy(self._base_units_cache[input_units])
916954
917 factor = 1.955 accumulators = [1., defaultdict(float)]
918 units = UnitsContainer()956 self._get_base_units(input_units, 1.0, accumulators)
919 for key, value in input_units.items():957
920 key = self.get_name(key)958 factor = accumulators[0]
921 reg = self._units[key]959 units = UnitsContainer(dict((k, v) for k, v in accumulators[1].items() if v != 0.))
922 if reg.is_base:
923 units.add(key, value)
924 else:
925 fac, uni = self.get_base_units(reg.reference, check_nonmult=False)
926 if factor is not None:
927 factor *= (reg.converter.scale * fac) ** value
928 units *= uni ** value
929960
930 # Check if any of the final units is non multiplicative and return None instead.961 # Check if any of the final units is non multiplicative and return None instead.
931 if check_nonmult:962 if check_nonmult:
932 for unit in units.keys():963 for unit in units.keys():
933 if not isinstance(self._units[unit].converter, ScaleConverter):964 if not self._units[unit].converter.is_multiplicative:
934 return None, units965 return None, units
935966
936 return factor, units967 return factor, units
937968
969 def _get_base_units(self, ref, exp, accumulators):
970 for key in ref:
971 exp2 = exp*ref[key]
972 key = self.get_name(key)
973 reg = self._units[key]
974 if reg.is_base:
975 accumulators[1][key] += exp2
976 else:
977 accumulators[0] *= reg._converter.scale ** exp2
978 if reg.reference is not None:
979 self._get_base_units(reg.reference, exp2, accumulators)
980
938 def get_compatible_units(self, input_units):981 def get_compatible_units(self, input_units):
939 if not input_units:982 if not input_units:
940 return 1., UnitsContainer()983 return 1., UnitsContainer()
@@ -967,9 +1010,9 @@
967 :return: converted value1010 :return: converted value
968 """1011 """
969 if isinstance(src, string_types):1012 if isinstance(src, string_types):
970 src = ParserHelper.from_string(src)1013 src = self.parse_units(src)
971 if isinstance(dst, string_types):1014 if isinstance(dst, string_types):
972 dst = ParserHelper.from_string(dst)1015 dst = self.parse_units(dst)
973 if src == dst:1016 if src == dst:
974 return value1017 return value
9751018
@@ -997,36 +1040,59 @@
997 if src_dim != dst_dim:1040 if src_dim != dst_dim:
998 raise DimensionalityError(src, dst, src_dim, dst_dim)1041 raise DimensionalityError(src, dst, src_dim, dst_dim)
9991042
1000 if len(src) == 1:1043 # Conversion needs to consider if non-multiplicative (AKA offset
1001 # If the source has a single element, it might be a non-multiplicative unit1044 # units) are involved. Conversion is only possible if src and dst
1002 # and therefore it is treated differently.1045 # have at most one offset unit per dimension.
1003 src_unit, src_value = list(src.items())[0]1046 src_offset_units = [(u, e) for u, e in src.items()
1004 src_unit = self._units[self.get_name(src_unit)]1047 if not self._units[u].is_multiplicative]
10051048 dst_offset_units = [(u, e) for u, e in dst.items()
1006 # We only continue if is a ScaleConverter,1049 if not self._units[u].is_multiplicative]
1007 # if not just exit to use the standard src / dst.1050
1008 # TODO: This will fail and should not degK * meter / nanometer -> degC1051 # For offset units we need to check if the conversion is allowed.
1009 if not isinstance(src_unit.converter, ScaleConverter):1052 if src_offset_units or dst_offset_units:
10101053
1011 if len(dst) > 1:1054 # Validate that not more than one offset unit is present
1012 # If the destination has more than one element,1055 if len(src_offset_units) > 1 or len(dst_offset_units) > 1:
1013 # then the conversion is not possible.1056 raise DimensionalityError(
1014 # TODO: This will fail and should not degC -> degK * meter / nanometer1057 src, dst, src_dim, dst_dim,
1015 raise DimensionalityError(src, dst, src_dim, dst_dim)1058 extra_msg=' - more than one offset unit.')
10161059
1017 dst_unit, dst_value = list(dst.items())[0]1060 # validate that offset unit is not used in multiplicative context
1018 dst_unit = self._units[self.get_name(dst_unit)]1061 if ((len(src_offset_units) == 1 and len(src) > 1)
1019 if not type(src_unit.converter) is type(dst_unit.converter):1062 or (len(dst_offset_units) == 1 and len(dst) > 1)
1020 raise DimensionalityError(src, dst, src_dim, dst_dim)1063 and not self.autoconvert_offset_to_baseunit):
10211064 raise DimensionalityError(
1022 return dst_unit.converter.from_reference(src_unit.converter.to_reference(value, inplace), inplace)1065 src, dst, src_dim, dst_dim,
10231066 extra_msg=' - offset unit used in multiplicative context.')
1067
1068 # Validate that order of offset unit is exactly one.
1069 if src_offset_units:
1070 if src_offset_units[0][1] != 1:
1071 raise DimensionalityError(
1072 src, dst, src_dim, dst_dim,
1073 extra_msg=' - offset units in higher order.')
1074 else:
1075 if dst_offset_units[0][1] != 1:
1076 raise DimensionalityError(
1077 src, dst, src_dim, dst_dim,
1078 extra_msg=' - offset units in higher order.')
1079
1080 # Here we convert only the offset quantities. Any remaining scaled
1081 # quantities will be converted later.
1082
1083 # clean src from offset units by converting to reference
1084 for u, e in src_offset_units:
1085 value = self._units[u].converter.to_reference(value, inplace)
1086 src.pop(u)
1087
1088 # clean dst units from offset units
1089 for u, e in dst_offset_units:
1090 dst.pop(u)
1091
1092 # Here src and dst have only multiplicative units left. Thus we can
1093 # convert with a factor.
1024 factor, units = self.get_base_units(src / dst)1094 factor, units = self.get_base_units(src / dst)
10251095
1026 if factor is None:
1027 raise DimensionalityError(src, dst, src_dim, dst_dim,
1028 'Non-multiplicative unit found.')
1029
1030 # factor is type float and if our magnitude is type Decimal then1096 # factor is type float and if our magnitude is type Decimal then
1031 # must first convert to Decimal before we can '*' the values1097 # must first convert to Decimal before we can '*' the values
1032 if isinstance(value, Decimal):1098 if isinstance(value, Decimal):
@@ -1037,6 +1103,16 @@
1037 else:1103 else:
1038 value = value * factor1104 value = value * factor
10391105
1106 # Finally convert to offset units specified in destination
1107 for u, e in dst_offset_units:
1108 value = self._units[u].converter.from_reference(value, inplace)
1109 # add back offset units to dst
1110 dst[u] = e
1111
1112 # restore offset conversion of src units
1113 for u, e in src_offset_units:
1114 src[u] = e
1115
1040 return value1116 return value
10411117
1042 def pi_theorem(self, quantities):1118 def pi_theorem(self, quantities):
@@ -1069,9 +1145,10 @@
1069 """Parse a unit to identify prefix, unit name and suffix1145 """Parse a unit to identify prefix, unit name and suffix
1070 by walking the list of prefix and suffix.1146 by walking the list of prefix and suffix.
1071 """1147 """
10721148 stw = unit_name.startswith
1073 for suffix, prefix in itertools.product(self._suffixes.keys(), self._prefixes.keys()):1149 edw = unit_name.endswith
1074 if unit_name.startswith(prefix) and unit_name.endswith(suffix):1150 for suffix, prefix in itertools.product(self._suffixes, self._prefixes):
1151 if stw(prefix) and edw(suffix):
1075 name = unit_name[len(prefix):]1152 name = unit_name[len(prefix):]
1076 if suffix:1153 if suffix:
1077 name = name[:-len(suffix)]1154 name = name[:-len(suffix)]
@@ -1079,30 +1156,33 @@
1079 continue1156 continue
1080 if case_sensitive:1157 if case_sensitive:
1081 if name in self._units:1158 if name in self._units:
1082 yield (self._prefixes[prefix].name,1159 yield (self._prefixes[prefix]._name,
1083 self._units[name].name,1160 self._units[name]._name,
1084 self._suffixes[suffix])1161 self._suffixes[suffix])
1085 else:1162 else:
1086 for real_name in self._units_casei.get(name.lower(), ()):1163 for real_name in self._units_casei.get(name.lower(), ()):
1087 yield (self._prefixes[prefix].name,1164 yield (self._prefixes[prefix]._name,
1088 self._units[real_name].name,1165 self._units[real_name]._name,
1089 self._suffixes[suffix])1166 self._suffixes[suffix])
10901167
1091 def parse_units(self, input_string, to_delta=None):1168 def parse_units(self, input_string, as_delta=None):
1092 """Parse a units expression and returns a UnitContainer with1169 """Parse a units expression and returns a UnitContainer with
1093 the canonical names.1170 the canonical names.
10941171
1095 The expression can only contain products, ratios and powers of units.1172 The expression can only contain products, ratios and powers of units.
10961173
1097 :param to_delta: if the expression has multiple units, the parser will1174 :param as_delta: if the expression has multiple units, the parser will
1098 interpret non multiplicative units as their `delta_` counterparts.1175 interpret non multiplicative units as their `delta_` counterparts.
10991176
1100 :raises:1177 :raises:
1101 :class:`pint.UndefinedUnitError` if a unit is not in the registry1178 :class:`pint.UndefinedUnitError` if a unit is not in the registry
1102 :class:`ValueError` if the expression is invalid.1179 :class:`ValueError` if the expression is invalid.
1103 """1180 """
1104 if to_delta is None:1181 if input_string in self._parse_unit_cache:
1105 to_delta = self.default_to_delta1182 return self._parse_unit_cache[input_string]
1183
1184 if as_delta is None:
1185 as_delta = self.default_as_delta
11061186
1107 if not input_string:1187 if not input_string:
1108 return UnitsContainer()1188 return UnitsContainer()
@@ -1113,16 +1193,19 @@
11131193
1114 ret = UnitsContainer()1194 ret = UnitsContainer()
1115 many = len(units) > 11195 many = len(units) > 1
1116 for name, value in units.items():1196 for name in units:
1117 cname = self.get_name(name)1197 cname = self.get_name(name)
1198 value = units[name]
1118 if not cname:1199 if not cname:
1119 continue1200 continue
1120 if to_delta and (many or (not many and abs(value) != 1)):1201 if as_delta and (many or (not many and value != 1)):
1121 definition = self._units[cname]1202 definition = self._units[cname]
1122 if not definition.is_multiplicative:1203 if not definition.is_multiplicative:
1123 cname = 'delta_' + cname1204 cname = 'delta_' + cname
1124 ret[cname] = value1205 ret[cname] = value
11251206
1207 self._parse_unit_cache[input_string] = ret
1208
1126 return ret1209 return ret
11271210
1128 def parse_expression(self, input_string, case_sensitive=True, **values):1211 def parse_expression(self, input_string, case_sensitive=True, **values):
@@ -1141,7 +1224,7 @@
1141 unknown = set()1224 unknown = set()
1142 for toknum, tokval, _, _, _ in gen:1225 for toknum, tokval, _, _, _ in gen:
1143 if toknum == NAME:1226 if toknum == NAME:
1144 # TODO: Integrate math better, Replace eval1227 # TODO: Integrate math better, Replace eval, make as_delta-aware
1145 if tokval == 'pi' or tokval in values:1228 if tokval == 'pi' or tokval in values:
1146 result.append((toknum, tokval))1229 result.append((toknum, tokval))
1147 continue1230 continue
11481231
=== modified file 'setup.cfg'
--- setup.cfg 2014-09-05 23:26:05 +0000
+++ setup.cfg 2015-01-07 15:51:38 +0000
@@ -1,7 +1,13 @@
1[check-manifest]1[check-manifest]
2ignore =2ignore =
3 .travis.yml3 .travis.yml
4 tox.ini4 tox.ini
55
6[bdist_wheel]6[bdist_wheel]
7universal = 17universal = 1
8
9[egg_info]
10tag_build =
11tag_date = 0
12tag_svn_revision = 0
13
814
=== modified file 'setup.py'
--- setup.py 2014-09-05 23:26:05 +0000
+++ setup.py 2015-01-07 15:51:38 +0000
@@ -29,7 +29,7 @@
2929
30setup(30setup(
31 name='Pint',31 name='Pint',
32 version='0.5.2',32 version='0.6',
33 description='Physical quantities module',33 description='Physical quantities module',
34 long_description=long_description,34 long_description=long_description,
35 keywords='physical quantities unit conversion science',35 keywords='physical quantities unit conversion science',
3636
=== removed file 'tox.ini'
--- tox.ini 2014-09-05 23:26:05 +0000
+++ tox.ini 1970-01-01 00:00:00 +0000
@@ -1,77 +0,0 @@
1[tox]
2envlist = py26,py27,py32,py33,numpy26,numpy27,numpy32,numpy33,
3 py26u,py27u,py32u,py33u,numpy26u,numpy27u,numpy32u,numpy33u
4
5[testenv]
6deps = coverage
7commands = coverage run -p --source=pint setup.py test
8
9setenv=
10 COVERAGE_FILE=.coverage.{envname}
11
12
13[testenv:py26]
14deps = unittest2
15 coverage
16
17[testenv:numpy26]
18deps = numpy
19 unittest2
20 coverage
21
22[testenv:numpy27]
23basepython=python2.7
24deps = numpy
25 coverage
26
27[testenv:numpy32]
28basepython=python3.2
29deps = numpy
30 coverage
31
32[testenv:numpy33]
33basepython=python3.3
34deps = numpy
35 coverage
36
37
38[testenv:py26u]
39deps = unittest2
40 uncertainties
41 coverage
42
43[testenv:py27u]
44deps = uncertainties
45 coverage
46
47[testenv:py32u]
48deps = uncertainties
49 coverage
50
51[testenv:py33u]
52deps = uncertainties
53 coverage
54
55[testenv:numpy26u]
56deps = numpy
57 unittest2
58 uncertainties
59 coverage
60
61[testenv:numpy27u]
62basepython=python2.7
63deps = numpy
64 uncertainties
65 coverage
66
67[testenv:numpy32u]
68basepython=python3.2
69deps = numpy
70 uncertainties
71 coverage
72
73[testenv:numpy33u]
74basepython=python3.3
75deps = numpy
76 uncertainties
77 coverage

Subscribers

People subscribed via source and target branches

to all changes: