diff --git a/LICENSE b/LICENSE index ba674fa..62c2beb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2020, QuatroPe +Copyright (c) 2020, Juan B Cabral and QuatroPe. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/MANIFEST.in b/MANIFEST.in index 8520016..f30965d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,6 @@ include LICENSE include README.md include CODE_OF_CONDUCT.md -recursive-exclude draft * recursive-exclude res * exclude test_*.py diff --git a/README.md b/README.md index 6147e2c..1935a70 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,29 @@ [![License](https://img.shields.io/pypi/l/uttrs?color=blue)](https://tldrlegal.com/license/bsd-3-clause-license-(revised)) [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://badge.fury.io/py/uttrs) - +**uttrs** is mainly two functions: - `uttr.ib` which generates attributes sensitive to units. - `uttr.array_accessor` which allows access to attributes linked to units, and transform them into numpy arrays. -The following piece of code is an exampl prototype of a Class representing a Galaxy. +## Installing + +- From PyPI + +```s +$ pip install uttrs +``` + +- Simply clone and from within the repo + +```s +$ pip install -e . +``` + + +## Quick Start + +The following piece of code is an example prototype of a Class representing a Galaxy. The Galaxy contains: - three arrays (`x`, `y`, `z`) with particle positions, measured in *kiloparsecs* (`u.kpc`). @@ -136,3 +153,12 @@ Lets fot example define `x` as a kilogram (`u.kg`) ValueError: Unit of attribute 'x' must be equivalent to 'kpc'.Found 'kg'. ``` + + +## References + +### Astropy + +> Price-Whelan, Adrian M., et al. "The Astropy project: + Building an open-science project and status of the v2. 0 core + package." The Astronomical Journal 156.3 (2018): 123. \ No newline at end of file diff --git a/draft/draft.ipynb b/draft/draft.ipynb deleted file mode 100644 index 3d9de25..0000000 --- a/draft/draft.ipynb +++ /dev/null @@ -1,231 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "import attr\n", - "import uttr\n", - "\n", - "import astropy.units as u\n", - "\n", - "@attr.s\n", - "class Galaxy:\n", - " x = uttr.ib(unit=u.kpc)\n", - " y = uttr.ib(unit=u.kpc)\n", - " z = uttr.ib(unit=u.kpc)\n", - "\n", - " vx = uttr.ib(unit=u.km/u.s)\n", - " vy = uttr.ib(unit=u.km/u.s)\n", - " vz = uttr.ib(unit=u.km/u.s)\n", - "\n", - " m = uttr.ib(unit=u.M_sun)\n", - "\n", - " notes = attr.ib(validator=attr.validators.instance_of(str))\n", - "\n", - " arr_ = uttr.array_accessor()\n", - "\n", - "\n", - "# creamos las particulas\n", - "x = np.random.randint(1000, 10_000, size=5) + np.random.rand(5)\n", - "y = np.random.randint(1000, 10_000, size=5) + np.random.rand(5)\n", - "z = np.random.randint(1000, 10_000, size=5) + np.random.rand(5)\n", - "vx = np.random.randint(1000, 10_000, size=5) + np.random.rand(5)\n", - "vy = np.random.randint(1000, 10_000, size=5) + np.random.rand(5)\n", - "vz = np.random.randint(1000, 10_000, size=5) + np.random.rand(5)\n", - "m = np.random.randint(1000, 10_000, size=5) + np.random.rand(5)\n", - "\n", - "gal = Galaxy(\n", - " x = x * u.kpc, # kpc is the sugested unit\n", - " y = y * u.mpc, # mpc is equivalent to kpc\n", - " z = z, # we asume is the sugested kpc unit\n", - " vx = vx * (u.km/u.s), # the sugested unit\n", - " vy = vy * (u.km/u.s), # the sugested unit\n", - " vz = vz, # the sugested unit\n", - " m = m * u.M_sun, # the sugested unit\n", - " notes=\"a random galaxy made with random numbers\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Galaxy(x=, y=, z=, vx=, vy=, vz=, m=, notes='a random galaxy made with random numbers')" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gal" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.00871237, 0.00969429, 0.00382184, 0.00918559, 0.00476001])" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.asarray(gal.y.to(u.kpc))" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.00871237, 0.00969429, 0.00382184, 0.00918559, 0.00476001])" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gal.arr_.y" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "''" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "repr(gal.y)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'a random galaxy made with random numbers'" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gal.arr_.notes" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Unit of attribute 'x' must be equivalent to 'kpc'.Found 'kg'.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mUnitConversionError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m~/proyectos/uttrs/src/uttr.py\u001b[0m in \u001b[0;36mis_equivalent\u001b[0;34m(self, instance, attribute, value)\u001b[0m\n\u001b[1;32m 81\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 82\u001b[0;31m \u001b[0munity\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 83\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUnitConversionError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/quantity.py\u001b[0m in \u001b[0;36mto\u001b[0;34m(self, unit, equivalencies)\u001b[0m\n\u001b[1;32m 688\u001b[0m \u001b[0munit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mUnit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 689\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_new_view\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_to_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mequivalencies\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 690\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/quantity.py\u001b[0m in \u001b[0;36m_to_value\u001b[0;34m(self, unit, equivalencies)\u001b[0m\n\u001b[1;32m 659\u001b[0m \u001b[0mequivalencies\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_equivalencies\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 660\u001b[0;31m return self.unit.to(unit, self.view(np.ndarray),\n\u001b[0m\u001b[1;32m 661\u001b[0m equivalencies=equivalencies)\n", - "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/core.py\u001b[0m in \u001b[0;36mto\u001b[0;34m(self, other, value, equivalencies)\u001b[0m\n\u001b[1;32m 986\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 987\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_converter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mequivalencies\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mequivalencies\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 988\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/core.py\u001b[0m in \u001b[0;36m_get_converter\u001b[0;34m(self, other, equivalencies)\u001b[0m\n\u001b[1;32m 917\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 918\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 919\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/core.py\u001b[0m in \u001b[0;36m_get_converter\u001b[0;34m(self, other, equivalencies)\u001b[0m\n\u001b[1;32m 902\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 903\u001b[0;31m return self._apply_equivalencies(\n\u001b[0m\u001b[1;32m 904\u001b[0m self, other, self._normalize_equivalencies(equivalencies))\n", - "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/core.py\u001b[0m in \u001b[0;36m_apply_equivalencies\u001b[0;34m(self, unit, other, equivalencies)\u001b[0m\n\u001b[1;32m 885\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 886\u001b[0;31m raise UnitConversionError(\n\u001b[0m\u001b[1;32m 887\u001b[0m \"{} and {} are not convertible\".format(\n", - "\u001b[0;31mUnitConversionError\u001b[0m: 'kg' (mass) and 'kpc' (length) are not convertible", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m gal = Galaxy(\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;31m# kpc is the sugested unit\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmpc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;31m# mpc is equivalent to kpc\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mz\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mz\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;31m# we asume is the sugested kpc unit\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mvx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvx\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkm\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;31m# the sugested unit\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, x, y, z, vx, vy, vz, m, notes, arr_)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marr_\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m__attr_factory_arr_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0m_config\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run_validators\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0m__attr_validator_x\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m__attr_x\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0m__attr_validator_y\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m__attr_y\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0m__attr_validator_z\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m__attr_z\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mz\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/attr/_make.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, inst, attr, value)\u001b[0m\n\u001b[1;32m 2721\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minst\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2722\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_validators\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2723\u001b[0;31m \u001b[0mv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2724\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2725\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/proyectos/uttrs/src/uttr.py\u001b[0m in \u001b[0;36mis_equivalent\u001b[0;34m(self, instance, attribute, value)\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUnitConversionError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[0munit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mufound\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattribute\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 85\u001b[0;31m raise ValueError(\n\u001b[0m\u001b[1;32m 86\u001b[0m \u001b[0;34mf\"Unit of attribute '{aname}' must be equivalent to '{unit}'.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 87\u001b[0m \u001b[0;34mf\"Found '{ufound}'.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: Unit of attribute 'x' must be equivalent to 'kpc'.Found 'kg'." - ] - } - ], - "source": [ - "gal = Galaxy(\n", - " x = x * u.kg, # kpc is the sugested unit\n", - " y = y * u.mpc, # mpc is equivalent to kpc\n", - " z = z, # we asume is the sugested kpc unit\n", - " vx = vx * (u.km/u.s), # the sugested unit\n", - " vy = vy * (u.km/u.s), # the sugested unit\n", - " vz = vz, # the sugested unit\n", - " m = m * u.M_sun, # the sugested unit\n", - " notes=\"a random galaxy made with random numbers\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.5" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/setup.py b/setup.py index d49a562..bd22e46 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2019, Juan B Cabral +# Copyright (c) 2020, Juan B Cabral and QuatroPe. # License: BSD-3-Clause # Full Text: https://github.com/quatrope/uttrs/blob/master/LICENSE @@ -66,7 +66,7 @@ description=DESCRIPTION, long_description=LONG_DESCRIPTION, long_description_content_type="text/markdown", - author="QuatroPe", + author="JB Cabral and QuatroPe", author_email="jbcabral@unc.edu.ar", url="https://github.com/quatrope/utts", license="3 Clause BSD", @@ -77,7 +77,7 @@ "units", "astropy", ], - classifiers=( + classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Education", "Intended Audience :: Science/Research", @@ -87,7 +87,7 @@ "Programming Language :: Python :: 3", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Scientific/Engineering", - ), + ], py_modules=["uttr", "ez_setup"], install_requires=REQUIREMENTS, ) diff --git a/test_uttr.py b/test_uttr.py index 47cdc3b..9c68051 100644 --- a/test_uttr.py +++ b/test_uttr.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2019, Juan B Cabral +# Copyright (c) 2020, Juan B Cabral and QuatroPe. # License: BSD-3-Clause # Full Text: https://github.com/quatrope/uttrs/blob/master/LICENSE @@ -24,7 +24,7 @@ import attr -# import numpy as np +import numpy as np import pytest @@ -74,14 +74,35 @@ def test_is_dimensionless(self, make_ucav): assert not ucav.is_dimensionless([1, 2, 3] * u.kg) assert not ucav.is_dimensionless(1 * u.kg) - def test_as_unit(self, make_ucav): + def test_convert_quantity(self, make_ucav): ucav = make_ucav(u.kg) - assert ucav.asunit(1).unit == u.kg - assert ucav.asunit(1 * u.kg).unit == u.kg - assert ucav.asunit(1 * u.m).unit == u.m + assert ucav.convert_quantity(1 * u.kg) == 1 * u.kg + assert ucav.convert_quantity(1 * u.g) == 0.001 * u.kg + + arr = [1, 2, 3] * u.g + np.testing.assert_array_equal( + ucav.convert_quantity(arr), [0.001, 0.002, 0.003] * u.kg + ) + with pytest.raises(u.UnitConversionError): + ucav.convert_quantity(1 * u.m) + + with pytest.raises(AttributeError): + ucav.convert_quantity(1) + + with pytest.raises(AttributeError): + ucav.convert_quantity([1]) + + with pytest.raises(AttributeError): + ucav.convert_quantity(np.array([1])) + + def test_convert_if_dimensionless(self, make_ucav): + ucav = make_ucav(u.kg) + assert ucav.convert_if_dimensionless(1).unit == u.kg + assert ucav.convert_if_dimensionless(1 * u.kg).unit == u.kg + assert ucav.convert_if_dimensionless(1 * u.m).unit == u.m arr = [1, 2, 3] * u.kg - assert ucav.asunit(arr) is arr + assert ucav.convert_if_dimensionless(arr) is arr def test_validate_is_equivalent_unit(self, make_ucav, attribute): ucav = make_ucav(u.kg) @@ -105,7 +126,9 @@ def test_validate_is_equivalent_unit(self, make_ucav, attribute): class TestAttributeFunction: def test_converter(self, make_cls): - with mock.patch("uttr.UnitConverterAndValidator.asunit") as asunit: + with mock.patch( + "uttr.UnitConverterAndValidator.convert_if_dimensionless" + ) as asunit: Cls = make_cls(foo=uttr.ib(unit=u.kg)) # check the converter @@ -117,7 +140,9 @@ def test_converter_with_another(self, make_cls): def fake_converter(v): return v - with mock.patch("uttr.UnitConverterAndValidator.asunit") as asunit: + with mock.patch( + "uttr.UnitConverterAndValidator.convert_if_dimensionless" + ) as asunit: Cls = make_cls(foo=uttr.ib(unit=u.kg, converter=fake_converter)) # check the converter diff --git a/uttr.py b/uttr.py index 79f8c37..af6d31f 100644 --- a/uttr.py +++ b/uttr.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2020, Juan B Cabral +# Copyright (c) 2020, Juan B Cabral and QuatroPe. # License: BSD-3-Clause # Full Text: https://github.com/quatrope/uttrs/blob/master/LICENSE @@ -10,7 +10,7 @@ # DOCS # ============================================================================= -"""uttrs bridge between attrs and Astropy units. +"""uttrs bridge between attrs and Astropy units [1]_. uttrs seeks to interoperate Classes defined using attrs and Astropy units in a simple manner with two main functionalities: @@ -19,6 +19,13 @@ - ``uttr.array_accessor`` which allows access to attributes linked to units, and transform them into numpy arrays. + +References +---------- +.. [1] Price-Whelan, Adrian M., et al. "The Astropy project: + Building an open-science project and status of the v2. 0 core + package." The Astronomical Journal 156.3 (2018): 123. + """ # ============================================================================= @@ -80,7 +87,11 @@ def is_dimensionless(self, v): not isinstance(v, u.Quantity) or v.unit == u.dimensionless_unscaled ) - def asunit(self, value): + def convert_quantity(self, v): + """Convert the quantity to the given unit.""" + return v.to(self.unit) + + def convert_if_dimensionless(self, value): """Assign a unit to a dimensionless object. If the object already has a dimension it returns it without change @@ -89,10 +100,11 @@ def asunit(self, value): -------- >>> uc = UnitConverter(u.km) - >>> uc.asunit(1) # dimensionless then convert + >>> uc.convert_if_dimensionless(1) # dimensionless then convert '' - >>> uc.asunit(1 * u.kpc) # with dimension the same object is returned + >>> # the same object is returned + >>> uc.convert_if_dimensionless(1 * u.kpc) '' """ @@ -186,7 +198,7 @@ def attribute(unit: u.UnitBase, **kwargs): converter = kwargs.pop("converter", []) if callable(converter): converter = [converter] - converter.append(ucav.asunit) + converter.append(ucav.convert_if_dimensionless) metadata = kwargs.pop("metadata", {}) metadata[UTTR_METADATA] = ucav @@ -256,7 +268,7 @@ def _coerce_default_unit(self, a, v): fd = self._fields_dict if a in fd and UTTR_METADATA in fd[a].metadata: ucav = fd[a].metadata[UTTR_METADATA] - return v.to(ucav.unit) + return ucav.convert_quantity(v) return v def __repr__(self):