Skip to content

Commit

Permalink
MAINT: vendor Tempita in scipy/_build_utils
Browse files Browse the repository at this point in the history
This is an unmodified copy from `Cython/Tempita/` as of commit
`023d4af35` in Cython's master branch (21 April 2024).
Tempita hasn't been maintained independently on PyPI for 10+ years;
we've used the Cython version which is semi-public (undocumented)
for a while, with the note that we should vendor it if that ever
fails. It now failed once (see scipy#20535), and conceptually that
makes sense - this is the only place where we don't invoke the
`cython` executable but actually do `import Cython`. That can fail
in a distro setup where Cython is installed for a different Python
interpreter than the active one.
  • Loading branch information
rgommers committed Apr 24, 2024
1 parent d55cb95 commit e2ef11e
Show file tree
Hide file tree
Showing 5 changed files with 1,257 additions and 4 deletions.
4 changes: 1 addition & 3 deletions scipy/_build_utils/tempita.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import os
import argparse

from Cython import Tempita as tempita
# XXX: If this import ever fails (does it really?), vendor either
# cython.tempita or numpy/npy_tempita.
import tempita


def process_tempita(fromfile, outfile=None):
Expand Down
4 changes: 4 additions & 0 deletions scipy/_build_utils/tempita/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# The original Tempita implements all of its templating code here.
# Moved it to _tempita.py to make the compilation portable.

from ._tempita import *
156 changes: 156 additions & 0 deletions scipy/_build_utils/tempita/_looper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"""
Helper for looping over sequences, particular in templates.
Often in a loop in a template it's handy to know what's next up,
previously up, if this is the first or last item in the sequence, etc.
These can be awkward to manage in a normal Python loop, but using the
looper you can get a better sense of the context. Use like::
>>> for loop, item in looper(['a', 'b', 'c']):
... print loop.number, item
... if not loop.last:
... print '---'
1 a
---
2 b
---
3 c
"""

basestring_ = (bytes, str)

__all__ = ['looper']


class looper:
"""
Helper for looping (particularly in templates)
Use this like::
for loop, item in looper(seq):
if loop.first:
...
"""

def __init__(self, seq):
self.seq = seq

def __iter__(self):
return looper_iter(self.seq)

def __repr__(self):
return '<%s for %r>' % (
self.__class__.__name__, self.seq)


class looper_iter:

def __init__(self, seq):
self.seq = list(seq)
self.pos = 0

def __iter__(self):
return self

def __next__(self):
if self.pos >= len(self.seq):
raise StopIteration
result = loop_pos(self.seq, self.pos), self.seq[self.pos]
self.pos += 1
return result


class loop_pos:

def __init__(self, seq, pos):
self.seq = seq
self.pos = pos

def __repr__(self):
return '<loop pos=%r at %r>' % (
self.seq[self.pos], self.pos)

def index(self):
return self.pos
index = property(index)

def number(self):
return self.pos + 1
number = property(number)

def item(self):
return self.seq[self.pos]
item = property(item)

def __next__(self):
try:
return self.seq[self.pos + 1]
except IndexError:
return None
__next__ = property(__next__)

def previous(self):
if self.pos == 0:
return None
return self.seq[self.pos - 1]
previous = property(previous)

def odd(self):
return not self.pos % 2
odd = property(odd)

def even(self):
return self.pos % 2
even = property(even)

def first(self):
return self.pos == 0
first = property(first)

def last(self):
return self.pos == len(self.seq) - 1
last = property(last)

def length(self):
return len(self.seq)
length = property(length)

def first_group(self, getter=None):
"""
Returns true if this item is the start of a new group,
where groups mean that some attribute has changed. The getter
can be None (the item itself changes), an attribute name like
``'.attr'``, a function, or a dict key or list index.
"""
if self.first:
return True
return self._compare_group(self.item, self.previous, getter)

def last_group(self, getter=None):
"""
Returns true if this item is the end of a new group,
where groups mean that some attribute has changed. The getter
can be None (the item itself changes), an attribute name like
``'.attr'``, a function, or a dict key or list index.
"""
if self.last:
return True
return self._compare_group(self.item, self.__next__, getter)

def _compare_group(self, item, other, getter):
if getter is None:
return item != other
elif (isinstance(getter, basestring_)
and getter.startswith('.')):
getter = getter[1:]
if getter.endswith('()'):
getter = getter[:-2]
return getattr(item, getter)() != getattr(other, getter)()
else:
return getattr(item, getter) != getattr(other, getter)
elif hasattr(getter, '__call__'):
return getter(item) != getter(other)
else:
return item[getter] != other[getter]

0 comments on commit e2ef11e

Please sign in to comment.