physpyx 2.3.0__tar.gz → 3.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: physpyx
3
- Version: 2.3.0
3
+ Version: 3.1.0
4
4
  Summary: Provides a way to use python code in LaTeX
5
5
  Author: Jérôme Dufour
6
6
  Author-email: Jérôme Dufour <jerome.dufour@eduvaud.ch>
7
7
  License-Expression: GPL-3.0-or-later
8
- Classifier: Programming Language :: Python :: 3
9
8
  Classifier: Operating System :: Unix
9
+ Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Topic :: Education
11
11
  Classifier: Topic :: Scientific/Engineering :: Physics
12
12
  Requires-Dist: numpy>=2.3.4
13
13
  Requires-Dist: pint>=0.25.1
14
14
  Requires-Dist: scipy>=1.17.0
15
15
  Requires-Python: >=3.13
16
- Project-URL: Repository, https://bitbucket.org/jdufour/physpyx/src/master/
17
16
  Project-URL: Changelog, https://bitbucket.org/jdufour/physpyx/src/master/CHANGELOG.md
18
17
  Project-URL: Documentation, https://physpyx.readthedocs.io/fr/latest/
18
+ Project-URL: Repository, https://bitbucket.org/jdufour/physpyx/src/master/
19
19
  Description-Content-Type: text/markdown
20
20
 
21
21
  # PhysPyX
@@ -1,16 +1,16 @@
1
1
  [project]
2
2
  name = "physpyx"
3
- version = "2.3.0"
3
+ version = "3.1.0"
4
4
  description = "Provides a way to use python code in LaTeX"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ license = "GPL-3.0-or-later"
5
8
  classifiers = [
6
- "Programming Language :: Python :: 3",
7
9
  "Operating System :: Unix",
10
+ "Programming Language :: Python :: 3",
8
11
  "Topic :: Education",
9
12
  "Topic :: Scientific/Engineering :: Physics",
10
13
  ]
11
- license = "GPL-3.0-or-later"
12
- readme = "README.md"
13
- requires-python = ">=3.13"
14
14
  dependencies = [
15
15
  "numpy>=2.3.4",
16
16
  "pint>=0.25.1",
@@ -22,27 +22,27 @@ name = "Jérôme Dufour"
22
22
  email = "jerome.dufour@eduvaud.ch"
23
23
 
24
24
  [project.urls]
25
- Repository = "https://bitbucket.org/jdufour/physpyx/src/master/"
26
25
  Changelog = "https://bitbucket.org/jdufour/physpyx/src/master/CHANGELOG.md"
27
26
  Documentation = "https://physpyx.readthedocs.io/fr/latest/"
27
+ Repository = "https://bitbucket.org/jdufour/physpyx/src/master/"
28
28
 
29
29
  [dependency-groups]
30
30
  dev = [
31
- "mypy>=1.18.1",
32
31
  "ipdb>=0.13.13",
33
- "scipy-stubs>=1.17.1.5",
32
+ "mypy>=2.3.0",
34
33
  "ruff>=0.16.0",
34
+ "scipy-stubs>=1.17.1.5",
35
35
  ]
36
36
  docs = [
37
+ "myst-parser>=4.0.1",
37
38
  "sphinx>=7.1.2",
38
- "sphinx-rtd-theme>=3.0.2",
39
39
  "sphinx-argparse>=0.5.2",
40
- "myst-parser>=4.0.1",
41
- "sphinx-autodoc2>=0.5.0",
42
40
  "sphinx-autobuild>=2025.8.25",
41
+ "sphinx-autodoc2>=0.5.0",
43
42
  "sphinx-design>=0.7.0",
43
+ "sphinx-rtd-theme>=3.0.2",
44
44
  ]
45
45
 
46
46
  [build-system]
47
- requires = ["uv_build>=0.11.0,<0.12.0"]
47
+ requires = ["uv_build>=0.12.0,<0.13.0"]
48
48
  build-backend = "uv_build"
@@ -1,47 +1,47 @@
1
1
  [project]
2
2
  name = "physpyx"
3
- version = "2.3.0"
3
+ version = "3.1.0"
4
+ description = "Provides a way to use python code in LaTeX"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ license = "GPL-3.0-or-later"
4
8
  authors = [
5
- { name="Jérôme Dufour", email="jerome.dufour@eduvaud.ch" },
9
+ { name = "Jérôme Dufour", email = "jerome.dufour@eduvaud.ch" },
6
10
  ]
7
- description = "Provides a way to use python code in LaTeX"
8
11
  classifiers = [
9
- "Programming Language :: Python :: 3",
10
- "Operating System :: Unix",
11
- "Topic :: Education",
12
- "Topic :: Scientific/Engineering :: Physics"
12
+ "Operating System :: Unix",
13
+ "Programming Language :: Python :: 3",
14
+ "Topic :: Education",
15
+ "Topic :: Scientific/Engineering :: Physics",
13
16
  ]
14
- license = "GPL-3.0-or-later"
15
- readme = "README.md"
16
- requires-python = ">=3.13"
17
17
  dependencies = [
18
18
  "numpy>=2.3.4",
19
19
  "pint>=0.25.1",
20
20
  "scipy>=1.17.0",
21
21
  ]
22
22
 
23
+ [project.urls]
24
+ Changelog = "https://bitbucket.org/jdufour/physpyx/src/master/CHANGELOG.md"
25
+ Documentation = "https://physpyx.readthedocs.io/fr/latest/"
26
+ Repository = "https://bitbucket.org/jdufour/physpyx/src/master/"
27
+
23
28
  [dependency-groups]
24
29
  dev = [
25
- "mypy>=1.18.1",
26
30
  "ipdb>=0.13.13",
27
- "scipy-stubs>=1.17.1.5",
31
+ "mypy>=2.3.0",
28
32
  "ruff>=0.16.0",
33
+ "scipy-stubs>=1.17.1.5",
29
34
  ]
30
35
  docs = [
31
- "sphinx>=7.1.2",
32
- "sphinx-rtd-theme>=3.0.2",
33
- "sphinx-argparse>=0.5.2",
34
- "myst-parser>=4.0.1",
35
- "sphinx-autodoc2>=0.5.0",
36
- "sphinx-autobuild>=2025.8.25",
37
- "sphinx-design>=0.7.0",
36
+ "myst-parser>=4.0.1",
37
+ "sphinx>=7.1.2",
38
+ "sphinx-argparse>=0.5.2",
39
+ "sphinx-autobuild>=2025.8.25",
40
+ "sphinx-autodoc2>=0.5.0",
41
+ "sphinx-design>=0.7.0",
42
+ "sphinx-rtd-theme>=3.0.2",
38
43
  ]
39
44
 
40
45
  [build-system]
41
- requires = ["uv_build>=0.11.0,<0.12.0"]
46
+ requires = ["uv_build>=0.12.0,<0.13.0"]
42
47
  build-backend = "uv_build"
43
-
44
- [project.urls]
45
- Repository = "https://bitbucket.org/jdufour/physpyx/src/master/"
46
- Changelog = "https://bitbucket.org/jdufour/physpyx/src/master/CHANGELOG.md"
47
- Documentation = "https://physpyx.readthedocs.io/fr/latest/"
@@ -1,7 +1,10 @@
1
1
  import re
2
2
  import sys
3
+ from collections.abc import Iterable
3
4
  from logging import getLogger
5
+ from typing import cast
4
6
 
7
+ from numpy import array
5
8
  from pint._typing import Scalar
6
9
  from pint.facets.context import Context
7
10
 
@@ -41,7 +44,7 @@ class Qty(PTManager):
41
44
  "\\ExplSyntaxOn\n\\prop_gset_from_keyval:Nn \\g_physpyx_pty_prop {\n"
42
45
  )
43
46
  for variable, qty in sys._getframe(2).f_locals.items():
44
- if isinstance(qty, SiunitxQuantity):
47
+ if variable != "_" and isinstance(qty, SiunitxQuantity):
45
48
  for name, new in qty.export(variable):
46
49
  outfile.write(f"\t{{{name}}} = {{{new}}},\n")
47
50
  if old := current.pop(name, None):
@@ -56,7 +59,7 @@ class Qty(PTManager):
56
59
 
57
60
  def __call__(
58
61
  self,
59
- value: Scalar | list[Scalar] | str,
62
+ value: Scalar | Iterable[Scalar | SiunitxQuantity] | str,
60
63
  unit: str | None = None,
61
64
  to: str | None = None,
62
65
  fmt: str = "g",
@@ -90,6 +93,22 @@ class Qty(PTManager):
90
93
  if unit is not None:
91
94
  logger.warning("Units are ignored when instanciating a constant")
92
95
  value, unit, fmt = self._constants(value, fmt)
96
+ elif isinstance(value, Iterable):
97
+ if all(isinstance(item, SiunitxQuantity) for item in value):
98
+ value = cast(Iterable[SiunitxQuantity], value)
99
+ dim = {v.dimensionality for v in value}
100
+ if len(dim) != 1:
101
+ raise ValueError(
102
+ f"Can't create a vector with different dimensionality ({dim})"
103
+ )
104
+
105
+ if unit == None:
106
+ unit = ""
107
+ if self._ureg.Unit(unit).dimensionality != dim.pop():
108
+ raise ValueError("Need to set unit with correct dimensionality")
109
+ value = array([v.fmtex(to=unit).m for v in value])
110
+ else:
111
+ value = array(value)
93
112
 
94
113
  q = self._ureg.Quantity(
95
114
  value,
@@ -1,8 +1,8 @@
1
- from collections.abc import Generator, Iterator
1
+ from collections.abc import Generator, Iterable, Iterator
2
2
  from logging import getLogger
3
3
  from typing import ClassVar, Self, TypedDict, cast
4
4
 
5
- from numpy import cross, linalg, ndarray
5
+ from numpy import cross, linalg
6
6
  from pint._typing import Magnitude, UnitLike
7
7
  from pint.registry import Quantity
8
8
 
@@ -30,12 +30,13 @@ class SiunitxQuantity(Quantity):
30
30
  "degree_Celsius": "celsius",
31
31
  "delta_degree_Celsius": "celsius",
32
32
  "electron_volt": "electronvolt",
33
+ "light_year": "lightyear",
34
+ "meter_Hg": "mHg",
33
35
  "metric_ton": "tonne",
34
36
  "revolutions_per_minute": "rpm",
35
37
  "speed_of_light": "celerity",
36
38
  "standard_atmosphere": "atm",
37
39
  "unified_atomic_mass_unit": "amu",
38
- "meter_Hg": "mHg",
39
40
  "watt_hour": "watthour",
40
41
  }
41
42
 
@@ -81,8 +82,8 @@ class SiunitxQuantity(Quantity):
81
82
  return alt
82
83
 
83
84
  def __iter__(self) -> Iterator["SiunitxQuantity"]:
84
- # NOTE: this is used to set formatting options in tuple expansions:
85
- # rx, ry = qty([3, 2], "kilometer", fmt=".1e")
85
+ # NOTE: this is used to set formatting options in tuple expansions rx,
86
+ # ry = qty([3, 2], "kilometer", fmt=".1e")
86
87
  for val in super().__iter__():
87
88
  val._fmt = self._fmt
88
89
  val._base = self._base
@@ -98,33 +99,35 @@ class SiunitxQuantity(Quantity):
98
99
  elif self._preferred:
99
100
  quantity = self._REGISTRY.to_preferred_units(self)
100
101
  if self._compact:
102
+ if isinstance(self.m, Iterable):
103
+ raise ValueError("'compact=True' for vectors has an undefined behavior")
101
104
  quantity = cast(SiunitxQuantity, self.to_compact())
102
105
 
103
106
  quantity._fmt = self._fmt
104
107
  return (
105
108
  quantity._format_array()
106
- if isinstance(quantity.m, ndarray)
109
+ if isinstance(quantity.m, Iterable)
107
110
  else quantity._format_scalar()
108
111
  )
109
112
 
113
+ def _format_magnitude(self, m: float) -> str:
114
+ out = f"{m:{self._fmt}}"
115
+ if out.startswith("1e"):
116
+ return out[1:]
117
+ elif out.startswith("-1e"):
118
+ return "-" + out[2:]
119
+ return out
120
+
110
121
  def _format_array(self) -> str:
111
122
  if self.m.ndim != 1:
112
123
  raise NotImplementedError
113
124
  out = r"\begin{pmatrix}"
114
125
  if isinstance(self.m[0], complex):
115
126
  out += "\\\\".join(
116
- [
117
- rf"\complexnum{{{v:{self._fmt}}}}" if abs(v) > 1e-16 else r"\num{0}"
118
- for v in self.m
119
- ]
127
+ rf"\complexnum{{{self._format_magnitude(m)}}}" for m in self.m
120
128
  )
121
129
  else:
122
- out += "\\\\".join(
123
- [
124
- rf"\num{{{v:{self._fmt}}}}" if abs(v) > 1e-16 else r"\num{0}"
125
- for v in self.m
126
- ]
127
- )
130
+ out += "\\\\".join(rf"\num{{{self._format_magnitude(m)}}}" for m in self.m)
128
131
 
129
132
  out += r"\end{pmatrix}"
130
133
  if ustr := self._format_unit():
@@ -132,9 +135,7 @@ class SiunitxQuantity(Quantity):
132
135
  return out
133
136
 
134
137
  def _format_scalar(self) -> str:
135
- out = f"{self.m:{self._fmt}}"
136
- if out.startswith("1e"):
137
- out = out[1:]
138
+ out = self._format_magnitude(self.m)
138
139
  if ustr := self._format_unit():
139
140
  if isinstance(self.m, complex):
140
141
  return rf"\complexqty{{{out}}}{{{ustr}}}"
@@ -156,6 +157,21 @@ class SiunitxQuantity(Quantity):
156
157
  # "PlainQuantity[Unknown]"'
157
158
  return cast(SiunitxQuantity, super().__add__(other))
158
159
 
160
+ def __truediv__(self, other: object) -> "SiunitxQuantity":
161
+ # NOTE: to fix typing warning 'Cannot access attribute "???" for class
162
+ # "PlainQuantity[Unknown]"'
163
+ return cast(SiunitxQuantity, super().__truediv__(other))
164
+
165
+ def __mul__(self, other: object) -> "SiunitxQuantity":
166
+ # NOTE: to fix typing warning 'Cannot access attribute "???" for class
167
+ # "PlainQuantity[Unknown]"'
168
+ return cast(SiunitxQuantity, super().__mul__(other))
169
+
170
+ def __rmul__(self, other: object) -> "SiunitxQuantity":
171
+ # NOTE: to fix typing warning 'Cannot access attribute "???" for class
172
+ # "PlainQuantity[Unknown]"'
173
+ return cast(SiunitxQuantity, super().__rmul__(other))
174
+
159
175
  def _format_unit(self) -> str:
160
176
  num = ""
161
177
  den = ""
@@ -2,6 +2,7 @@ from typing import Any, cast
2
2
 
3
3
  from pint.facets.context import Context
4
4
  from pint.registry import GenericUnitRegistry, Unit
5
+ from pint.util import UnitsContainer
5
6
 
6
7
  from physpyx.context import SiunitxContext
7
8
  from physpyx.quantity import SiunitxQuantity
@@ -23,31 +24,32 @@ class SiunitxUnitRegistry(GenericUnitRegistry):
23
24
  self.define("an = year")
24
25
  self.define("ans = years")
25
26
  self.define("celerity = speed_of_light")
26
- self.define("chf = [currency] = franc")
27
- self.define("ct = 0.01 * chf = centime")
27
+ self.define("swissfranc = [currency]")
28
+ self.define("swisscent = swissfranc * 0.01")
28
29
  self._preferred_units = {
29
30
  Unit(name).dimensionality: name
30
31
  for name in [
31
- "meter",
32
- "second",
33
- "kilogram",
34
32
  "ampere",
33
+ "coulomb",
34
+ "farad",
35
+ "henry",
36
+ "joule",
35
37
  "kelvin",
36
- "mole",
37
- "meter per second",
38
+ "kilogram",
38
39
  "meter per second squared",
40
+ "meter per second",
41
+ "meter",
42
+ "mole",
39
43
  "newton",
40
- "joule",
41
- "watt",
42
- "pascal",
43
- "volt",
44
44
  "ohm",
45
+ "pascal",
46
+ "second",
45
47
  "tesla",
46
- "henry",
47
- "farad",
48
- "coulomb",
48
+ "volt",
49
+ "watt",
49
50
  ]
50
51
  }
52
+ self._preferred_units[UnitsContainer({"[currency]": 1})] = "swissfranc"
51
53
 
52
54
  def enable_contexts(self, *names_or_contexts: str | Context, **kwargs: Any) -> None:
53
55
  for context in names_or_contexts:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes