Skip to content

Commit fd3f340

Browse files
authored
Merge pull request #114 from Kai-Striega/experiment/adopt-spin-and-cython
BLD: Build using spin
2 parents 9080caf + 69b8db7 commit fd3f340

File tree

11 files changed

+164
-105
lines changed

11 files changed

+164
-105
lines changed

.github/workflows/lint.yml

Lines changed: 0 additions & 53 deletions
This file was deleted.

.github/workflows/pythonpackage.yml

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,41 @@
1-
name: Python package
1+
name: Test package
22

33
on: [push, pull_request]
44

55
jobs:
66
build:
77
runs-on: ${{ matrix.os }}
8+
defaults:
9+
run:
10+
shell: bash -el {0}
811
strategy:
912
matrix:
1013
os: [ubuntu-latest, macos-latest, windows-latest]
1114
python-version: ["3.10", "3.11", "3.12"]
1215
steps:
1316
- uses: actions/checkout@v3
1417
- name: Set up Python ${{ matrix.python-version }}
15-
uses: actions/setup-python@v4
18+
uses: conda-incubator/setup-miniconda@v3
1619
with:
20+
auto-update-conda: true
1721
python-version: ${{ matrix.python-version }}
18-
- name: Install dependencies
22+
activate-environment: numpy-financial-dev
23+
environment-file: environment.yml
24+
auto-activate-base: false
25+
- name: Conda metadata
1926
run: |
20-
python -m pip install --upgrade pip poetry
21-
poetry env use ${{ matrix.python-version }}
22-
poetry install --with=test
27+
conda info
28+
conda list
29+
- name: Lint
30+
run: |
31+
set -euo pipefail
32+
# Tell us what version we are using
33+
ruff version
34+
# Check the source file, ignore type annotations (ANN) for now.
35+
ruff check numpy_financial/ benchmarks/ --ignore F403 --select E,F,B,I
36+
- name: Build project
37+
run: |
38+
spin build -v
2339
- name: Test with pytest
2440
run: |
25-
poetry run pytest --doctest-modules
41+
spin test -- --doctest-modules

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ GTAGS
6262
################
6363
# setup.py working directory
6464
build
65+
build-install
6566
# sphinx build directory
6667
_build
6768
# setup.py dist directory
@@ -112,3 +113,7 @@ poetry.lock
112113
# Things specific to this project #
113114
###################################
114115
doc/source/_api_stubs
116+
117+
# Misc files that we do not require #
118+
.asv/
119+
.hypothesis/

environment.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# To use:
2+
# $ conda env create -f environment.yml # `mamba` works too for this command
3+
# $ conda activate numpy-financial-dev
4+
#
5+
name: numpy-financial-dev
6+
channels:
7+
- conda-forge
8+
dependencies:
9+
# Runtime dependencies
10+
- python
11+
- numpy
12+
# Build
13+
- cython>=3.0.9
14+
- compilers
15+
- meson
16+
- meson-python
17+
- ninja
18+
# Tests
19+
- pytest
20+
- pytest-xdist
21+
- asv>=0.6.0
22+
- hypothesis
23+
# Docs
24+
- myst-parser
25+
- numpydoc
26+
- pydata-sphinx-theme>=0.15.0
27+
- spin
28+
# Lint
29+
- ruff>=0.3.0
30+
# Benchmarks
31+
- asv>=0.6.0

meson.build

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
project(
2+
'numpy-financial',
3+
'cython', 'c',
4+
version: '2.0.0.dev',
5+
license: 'BSD-3',
6+
meson_version: '>=1.4.0',
7+
)
8+
9+
cc = meson.get_compiler('c')
10+
cy = meson.get_compiler('cython')
11+
12+
if not cy.version().version_compare('>=3.0.9')
13+
error('NumPy-Financial requires Cython >= 3.0.9')
14+
endif
15+
16+
py = import('python').find_installation(pure: false)
17+
py_dep = py.dependency()
18+
19+
subdir('numpy_financial')

numpy_financial/_cfinancial.pyx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from libc.math cimport NAN
2+
cimport cython
3+
4+
@cython.boundscheck(False)
5+
@cython.cdivision(True)
6+
def npv(const double[::1] rates, const double[:, ::1] values, double[:, ::1] out):
7+
cdef:
8+
Py_ssize_t i, j, t
9+
double acc
10+
11+
with nogil:
12+
for i in range(rates.shape[0]):
13+
for j in range(values.shape[0]):
14+
acc = 0.0
15+
for t in range(values.shape[1]):
16+
if rates[i] == -1.0:
17+
acc = NAN
18+
break
19+
else:
20+
acc = acc + values[j, t] / ((1.0 + rates[i]) ** t)
21+
out[i, j] = acc

numpy_financial/_financial.py

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313

1414
from decimal import Decimal
1515

16-
import numba as nb
1716
import numpy as np
1817

18+
from . import _cfinancial
19+
1920
__all__ = ['fv', 'pmt', 'nper', 'ipmt', 'ppmt', 'pv', 'rate',
2021
'irr', 'npv', 'mirr',
2122
'NoRealSolutionError', 'IterationsExceededError']
@@ -844,20 +845,6 @@ def irr(values, *, raise_exceptions=False):
844845
return eirr[np.argmin(abs_eirr)]
845846

846847

847-
@nb.njit
848-
def _npv_native(rates, values, out):
849-
for i in range(rates.shape[0]):
850-
for j in range(values.shape[0]):
851-
acc = 0.0
852-
for t in range(values.shape[1]):
853-
if rates[i] == -1.0:
854-
acc = np.nan
855-
break
856-
else:
857-
acc += values[j, t] / ((1.0 + rates[i]) ** t)
858-
out[i, j] = acc
859-
860-
861848
def npv(rate, values):
862849
r"""Return the NPV (Net Present Value) of a cash flow series.
863850
@@ -935,8 +922,8 @@ def npv(rate, values):
935922
[-2798.19, -3612.24],
936923
[-2884.3 , -3710.74]])
937924
"""
938-
values_inner = np.atleast_2d(values)
939-
rate_inner = np.atleast_1d(rate)
925+
values_inner = np.atleast_2d(values).astype(np.float64)
926+
rate_inner = np.atleast_1d(rate).astype(np.float64)
940927

941928
if rate_inner.ndim != 1:
942929
msg = "invalid shape for rates. Rate must be either a scalar or 1d array"
@@ -948,7 +935,7 @@ def npv(rate, values):
948935

949936
output_shape = _get_output_array_shape(rate_inner, values_inner)
950937
out = np.empty(output_shape)
951-
_npv_native(rate_inner, values_inner, out)
938+
_cfinancial.npv(rate_inner, values_inner, out)
952939
return _ufunc_like(out)
953940

954941

numpy_financial/meson.build

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
py.extension_module(
2+
'_cfinancial',
3+
'_cfinancial.pyx',
4+
install: true,
5+
subdir: 'numpy_financial',
6+
)
7+
8+
python_sources = [
9+
'__init__.py',
10+
'_financial.py',
11+
]
12+
13+
py.install_sources(
14+
python_sources,
15+
subdir: 'numpy_financial',
16+
)
17+
18+
install_subdir('tests', install_dir: py.get_install_dir() / 'numpy_financial')
File renamed without changes.

pyproject.toml

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
[build-system]
2-
requires = ["poetry-core"]
3-
build-backend = "poetry.core.masonry.api"
4-
2+
build-backend = "mesonpy"
3+
requires = [
4+
"meson-python>=0.15.0",
5+
"Cython>=3.0.9",
6+
"numpy>=1.23.5",
7+
]
58

6-
[tool.poetry]
9+
[project]
710
name = "numpy-financial"
811
version = "2.0.0"
12+
requires-python = ">=3.10"
913
description = "Simple financial functions"
1014
license = "BSD-3-Clause"
1115
authors = ["Travis E. Oliphant et al."]
@@ -34,30 +38,41 @@ classifiers = [
3438
"Operating System :: Unix",
3539
"Operating System :: MacOS",
3640
]
37-
packages = [{include = "numpy_financial"}]
38-
39-
[tool.poetry.dependencies]
40-
python = "^3.10"
41-
numpy = "^1.23"
42-
numba = "^0.59.1"
43-
44-
[tool.poetry.group.test.dependencies]
45-
pytest = "^8.0"
46-
hypothesis = {extras = ["numpy"], version = "^6.99.11"}
47-
pytest-xdist = {extras = ["psutil"], version = "^3.5.0"}
48-
49-
50-
[tool.poetry.group.docs.dependencies]
51-
sphinx = "^7.0"
52-
numpydoc = "^1.5"
53-
pydata-sphinx-theme = "^0.15"
54-
myst-parser = "^2.0.0"
55-
5641

42+
[project.optional-dependencies]
43+
test = [
44+
"pytest",
45+
"pytest-xdist",
46+
"hypothesis",
47+
]
48+
doc = [
49+
"sphinx>=7.0",
50+
"numpydoc>=1.5",
51+
"pydata-sphinx-theme>=0.15",
52+
"myst-parser>=2.0.0",
53+
]
54+
dev = [
55+
"ruff>=0.3.0",
56+
"asv>=0.6.0",
57+
]
5758

58-
[tool.poetry.group.lint.dependencies]
59-
ruff = "^0.3"
6059

60+
[tool.spin]
61+
package = 'numpy_financial'
6162

62-
[tool.poetry.group.bench.dependencies]
63-
asv = "^0.6"
63+
[tool.spin.commands]
64+
"Build" = [
65+
"spin.cmds.meson.build",
66+
"spin.cmds.meson.test",
67+
"spin.cmds.build.sdist",
68+
"spin.cmds.pip.install",
69+
]
70+
"Documentation" = [
71+
"spin.cmds.meson.docs"
72+
]
73+
"Environments" = [
74+
"spin.cmds.meson.shell",
75+
"spin.cmds.meson.ipython",
76+
"spin.cmds.meson.python",
77+
"spin.cmds.meson.run"
78+
]

0 commit comments

Comments
 (0)