Skip to content

Commit 5b39a5d

Browse files
committed
Merge branch 'releases/v.1.7.1' into pr/196
2 parents ed08616 + 949e114 commit 5b39a5d

29 files changed

+973
-270
lines changed

.github/workflows/python-publish.yml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This workflow will upload a Python Package using Twine when a release is created
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
3+
4+
# This workflow uses actions that are not certified by GitHub.
5+
# They are provided by a third-party and are governed by
6+
# separate terms of service, privacy policy, and support
7+
# documentation.
8+
9+
name: Upload Python Package
10+
11+
on:
12+
release:
13+
types: [published]
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
deploy:
20+
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- uses: actions/checkout@v3
25+
- name: Set up Python
26+
uses: actions/setup-python@v3
27+
with:
28+
python-version: '3.x'
29+
- name: Install dependencies
30+
run: |
31+
python -m pip install --upgrade pip
32+
python -m pip install flit
33+
flit install
34+
- name: Build package
35+
run: |
36+
flit build
37+
- name: Publish package
38+
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
39+
with:
40+
user: __token__
41+
password: ${{ secrets.PYPI_API_TOKEN }}

.github/workflows/python-pytest.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3+
4+
name: Python testing
5+
6+
on:
7+
push:
8+
branches: [ "main" ]
9+
pull_request:
10+
branches: [ "main" ]
11+
12+
jobs:
13+
build:
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
os: [macos-latest, windows-latest, ubuntu-latest]
18+
python-version: ["3.10", "3.11", "3.12"]
19+
runs-on: ${{ matrix.os }}
20+
steps:
21+
- uses: actions/checkout@v3
22+
- name: Set up Python ${{ matrix.python-version }}
23+
uses: actions/setup-python@v3
24+
with:
25+
python-version: ${{ matrix.python-version }}
26+
- name: Install dependencies
27+
run: |
28+
python -m pip install --upgrade pip
29+
python -m pip install flit
30+
flit install
31+
- name: Test black formatted
32+
run: |
33+
black handcalcs --check
34+
- name: Test with pytest
35+
run: |
36+
pytest

README.md

+52-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
<img src="https://img.shields.io/pypi/v/handcalcs">
66
<img src="https://img.shields.io/pypi/pyversions/handcalcs">
77
<img src="https://img.shields.io/github/license/connorferster/handcalcs">
8-
<img src="https://img.shields.io/pypi/dm/handcalcs">
8+
<img src="https://static.pepy.tech/badge/handcalcs">
99
</p>
1010
<p align="center">
1111
<img src="docs/images/handcalcs.jpg"><br>
12-
Covert art by <a href = "https://www.instagram.com/joshuahoibergtattoos/">Joshua Hoiberg</a>
12+
Covert art by <a href = "https://www.copperkettlegameworks.ca/">Joshua Hoiberg</a>
1313
</p>
1414

1515
<h1 align = "center">handcalcs:<br>Python calculations in Jupyter,<br>as though you wrote them by hand.</h1>
@@ -85,7 +85,7 @@ import handcalcs.render
8585
from math import sqrt, pi
8686
```
8787

88-
Now, you can use the `%%tex` magic!
88+
Now, you can also use the `%%tex` magic!
8989

9090
```python
9191
%%tex
@@ -129,6 +129,55 @@ Used in this way, you can use `@handcalc()` to dynamically generate Latex code f
129129

130130
![Parameters](docs/images/decorator.png)
131131

132+
---
133+
134+
## Global config options (New in v1.6.0)
135+
136+
This is a major new release for handcalcs and introduces the global configuration feature. This allows users to have control over several options of how handcalcs works. The configuration options, with their default values, are as follow:
137+
138+
* `decimal_separator = "."`
139+
* `latex_block_start = "\\["`
140+
* `latex_block_end = "\\]"`
141+
* `math_environment_start = "aligned"`
142+
* `math_environment_end = "aligned"`
143+
* `line_break = "\\\\[10pt]"`
144+
* `use_scientific_notation = False`
145+
* `display_precision = 3`
146+
* `underscore_subscripts = True`
147+
* `greek_exclusions = []`
148+
* `param_columns = 3`
149+
* `preferred_string_formatter = "L"`
150+
* `custom_symbols = {}`
151+
152+
### Config API
153+
154+
```python
155+
import handcalcs.render
156+
157+
handcalcs.set_option("display_precision", 4)
158+
handcalcs.set_option("param_columns", 5)
159+
handcalcs.set_option("line_break", "\\\\[20pt]")
160+
handcalcs.set_option("greek_exclusions", ["psi"]) # etc...
161+
```
162+
These changes now affect all cells rendered in the current session. If you want to permanently update the `config.json` file with these changes (so handcalcs will always load up with these options), you can then call `handcalcs.save_config()` and the changes will be saved (and thus immediately available in the next session).
163+
164+
#### Custom Symbols (New in v1.7.0)
165+
166+
You can now add _custom symbols_ to your global config to handle ALL of the cases which handcalcs does not account for.
167+
168+
e.g.
169+
170+
```python
171+
handcalcs.set_option("custom_symbols", {"V_dot": "\\dot{V}", "N_star": "N^{*}"})
172+
```
173+
174+
Will now allow this kind of rendering:
175+
176+
![Custom symbols example showing the use of V_dot and N_star](docs/images/custom_symbols.png)
177+
178+
The docstring in the `handcalcs.set_option()` function demonstrates which options are available and what values they take.
179+
---
180+
132181

133182
## Override tags
134183

docs/images/custom_symbols.png

15.4 KB
Loading

handcalcs/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"""
1515
Render arithmetic calucations in Jupyter as though they were written by hand.
1616
"""
17-
__version__ = "1.6.5" #
17+
__version__ = "1.7.1" #
1818
from .decorator import handcalc
1919
from .global_config import set_option, save_config
2020

handcalcs/config.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
"underscore_subscripts": true,
1111
"greek_exclusions": [],
1212
"param_columns": 3,
13-
"preferred_string_formatter": "L"
13+
"preferred_string_formatter": "L",
14+
"custom_symbols": {}
1415
}

handcalcs/global_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def _load_global_config(config_file_name: str):
3737
str_value = str_value.replace("\n", "\\n")
3838
else:
3939
str_value = str(value)
40-
_OPTIONS.append(f"{key} -> {type(value)} (default = {str_value})")
40+
_OPTIONS.append(f"{key}: {type(value)} (default = {str_value})")
4141
# _OPTIONS = [f"{key} -> {type(value)} (default = {value})" for key, value in _config.items()]
4242
_OPTIONS_TEXT = "Configuration can be set on the following options:\n\t" + "\n\t".join(
4343
_OPTIONS

handcalcs/handcalcs.py

+52-10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from handcalcs import global_config
3232
from handcalcs.integrations import DimensionalityError
3333

34+
3435
# Six basic line types
3536
@dataclass
3637
class CalcLine:
@@ -2051,6 +2052,7 @@ def swap_symbolic_calcs(
20512052
symbolic_expression = copy.copy(calculation)
20522053
functions_on_symbolic_expressions = [
20532054
insert_parentheses,
2055+
swap_custom_symbols,
20542056
swap_math_funcs,
20552057
swap_superscripts,
20562058
swap_chained_fracs,
@@ -2060,6 +2062,7 @@ def swap_symbolic_calcs(
20602062
swap_for_greek,
20612063
swap_prime_notation,
20622064
swap_long_var_strs,
2065+
swap_double_subscripts,
20632066
extend_subscripts,
20642067
swap_superscripts,
20652068
flatten_deque,
@@ -2095,11 +2098,12 @@ def swap_numeric_calcs(
20952098
swap_for_greek,
20962099
swap_prime_notation,
20972100
swap_superscripts,
2101+
swap_double_subscripts,
20982102
extend_subscripts,
20992103
flatten_deque,
21002104
]
21012105
for function in functions_on_numeric_expressions:
2102-
if function is swap_values or function is swap_math_funcs:
2106+
if function in (swap_values, swap_math_funcs):
21032107
numeric_expression = function(
21042108
numeric_expression, calc_results, **config_options
21052109
)
@@ -2146,6 +2150,28 @@ def swap_integrals(d: deque, calc_results: dict, **config_options) -> deque:
21462150
return d
21472151

21482152

2153+
def swap_custom_symbols(d: deque, **config_options) -> deque:
2154+
"""
2155+
Swaps the custom symbols from the 'config_options'.
2156+
"""
2157+
swapped_items = deque([])
2158+
for item in d:
2159+
if isinstance(item, deque):
2160+
new_item = swap_custom_symbols(item, **config_options)
2161+
swapped_items.append(new_item)
2162+
elif isinstance(item, str):
2163+
custom_symbols = config_options.get("custom_symbols", {})
2164+
new_item = item
2165+
for symbol, latex_symbol in custom_symbols.items():
2166+
if symbol in item:
2167+
new_item = item.replace(symbol, latex_symbol)
2168+
break
2169+
swapped_items.append(new_item)
2170+
else:
2171+
swapped_items.append(item)
2172+
return swapped_items
2173+
2174+
21492175
def swap_log_func(d: deque, calc_results: dict, **config_options) -> deque:
21502176
"""
21512177
Returns a new deque representing 'd' but with any log functions swapped
@@ -2341,6 +2367,23 @@ def list_to_deque(los: List[str]) -> deque:
23412367
return acc
23422368

23432369

2370+
def swap_double_subscripts(pycode_as_deque: deque, **config_options) -> deque:
2371+
"""
2372+
For variables or function names that contain a double subscript '__',
2373+
the double subscript will be replaced with LaTeX space: "\\ "
2374+
"""
2375+
swapped_deque = deque([])
2376+
for item in pycode_as_deque:
2377+
if isinstance(item, deque):
2378+
new_item = swap_double_subscripts(item)
2379+
elif isinstance(item, str) and "__" in item:
2380+
new_item = item.replace("__", "\\ ")
2381+
else:
2382+
new_item = item
2383+
swapped_deque.append(new_item)
2384+
return swapped_deque
2385+
2386+
23442387
def extend_subscripts(pycode_as_deque: deque, **config_options) -> deque:
23452388
"""
23462389
For variables named with a subscript, e.g. V_c, this function ensures that any
@@ -2355,7 +2398,7 @@ def extend_subscripts(pycode_as_deque: deque, **config_options) -> deque:
23552398
new_item = extend_subscripts(item) # recursion!
23562399
swapped_deque.append(new_item)
23572400
elif isinstance(item, str) and "_" in item and not "\\int" in item:
2358-
if "\\mathrm{" in item:
2401+
if "\\mathrm{" in item or "\\operatorname{" in item:
23592402
discount = 1
23602403
new_item = ""
23612404
for char in item:
@@ -2543,15 +2586,12 @@ def swap_math_funcs(
25432586
elif poss_func_name == "ceil" or poss_func_name == "floor":
25442587
new_item = swap_floor_ceil(item, poss_func_name, calc_results)
25452588
swapped_deque.append(new_item)
2546-
#
2547-
# elif possible_func and poss_func_name:
2548-
# elif possible_func:
25492589
elif possible_func:
25502590
ops = "\\operatorname"
25512591
new_func = f"{ops}{a}{poss_func_name}{b}"
25522592
item = swap_func_name(item, poss_func_name, new_func)
2553-
if possible_func:
2554-
item = insert_func_braces(item)
2593+
# if possible_func:
2594+
# item = insert_func_braces(item)
25552595
new_item = swap_math_funcs(item, calc_results)
25562596
swapped_deque.append(new_item)
25572597

@@ -2838,9 +2878,11 @@ def swap_for_greek(pycode_as_deque: deque, **config_options) -> deque:
28382878
elif "_" in str(item):
28392879
components = str(item).split("_")
28402880
swapped_components = [
2841-
dict_get(greek_chainmap, component)
2842-
if component not in greeks_to_exclude
2843-
else component
2881+
(
2882+
dict_get(greek_chainmap, component)
2883+
if component not in greeks_to_exclude
2884+
else component
2885+
)
28442886
for component in components
28452887
]
28462888
new_item = "_".join(swapped_components)

pyproject.toml

+6-6
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ classifiers = ["License :: OSI Approved :: Apache Software License"]
1111
dynamic = ["version", "description"]
1212
dependencies = [
1313
"more_itertools",
14-
"nbconvert >= 6.0",
15-
"innerscope",
14+
"nbconvert >= 7.0.0",
15+
"innerscope >= 0.7.0",
1616
"pyparsing"
1717
]
1818

@@ -23,14 +23,14 @@ Source = "https://github.com/connorferster/handcalcs"
2323
[project.optional-dependencies]
2424

2525
test = [
26-
"pytest >= 6.0.0",
27-
"pytest-cov >= 2.9.0",
26+
"pytest >= 8.0.0",
27+
"pytest-cov >= 4.1.0",
2828
"coverage[toml] >= 5.5.0",
29-
"pint >= 0.18",
29+
"pint >= 0.23",
3030
]
3131

3232
dev = [
33-
"jupyterlab >= 3.0.0",
33+
"jupyterlab >= 4.0.0",
3434
"sympy",
3535
"forallpeople >= 2.0",
3636
"black"

0 commit comments

Comments
 (0)