Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/patsy/origin.py : 21%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Patsy
2# Copyright (C) 2011-2012 Nathaniel Smith <njs@pobox.com>
3# See file LICENSE.txt for license information.
5# The core 'origin' tracking system. This point of this is to have machinery
6# so if some object is ultimately derived from some portion of a string (e.g.,
7# a formula), then we can keep track of that, and use it to give proper error
8# messages.
10# These are made available in the patsy.* namespace
11__all__ = ["Origin"]
13class Origin(object):
14 """This represents the origin of some object in some string.
16 For example, if we have an object ``x1_obj`` that was produced by parsing
17 the ``x1`` in the formula ``"y ~ x1:x2"``, then we conventionally keep
18 track of that relationship by doing::
20 x1_obj.origin = Origin("y ~ x1:x2", 4, 6)
22 Then later if we run into a problem, we can do::
24 raise PatsyError("invalid factor", x1_obj)
26 and we'll produce a nice error message like::
28 PatsyError: invalid factor
29 y ~ x1:x2
30 ^^
32 Origins are compared by value, and hashable.
33 """
35 def __init__(self, code, start, end):
36 self.code = code
37 self.start = start
38 self.end = end
40 @classmethod
41 def combine(cls, origin_objs):
42 """Class method for combining a set of Origins into one large Origin
43 that spans them.
45 Example usage: if we wanted to represent the origin of the "x1:x2"
46 term, we could do ``Origin.combine([x1_obj, x2_obj])``.
48 Single argument is an iterable, and each element in the iterable
49 should be either:
51 * An Origin object
52 * ``None``
53 * An object that has a ``.origin`` attribute which fulfills the above
54 criteria.
56 Returns either an Origin object, or None.
57 """
58 origins = []
59 for obj in origin_objs:
60 if obj is not None and not isinstance(obj, Origin):
61 obj = obj.origin
62 if obj is None:
63 continue
64 origins.append(obj)
65 if not origins:
66 return None
67 codes = set([o.code for o in origins])
68 assert len(codes) == 1
69 start = min([o.start for o in origins])
70 end = max([o.end for o in origins])
71 return cls(codes.pop(), start, end)
73 def relevant_code(self):
74 """Extracts and returns the span of the original code represented by
75 this Origin. Example: ``x1``."""
76 return self.code[self.start:self.end]
78 def __eq__(self, other):
79 return (isinstance(other, Origin)
80 and self.code == other.code
81 and self.start == other.start
82 and self.end == other.end)
84 def __ne__(self, other):
85 return not self == other
87 def __hash__(self):
88 return hash((Origin, self.code, self.start, self.end))
90 def caretize(self, indent=0):
91 """Produces a user-readable two line string indicating the origin of
92 some code. Example::
94 y ~ x1:x2
95 ^^
97 If optional argument 'indent' is given, then both lines will be
98 indented by this much. The returned string does not have a trailing
99 newline.
100 """
101 return ("%s%s\n%s%s%s"
102 % (" " * indent,
103 self.code,
104 " " * indent,
105 " " * self.start,
106 "^" * (self.end - self.start)))
108 def __repr__(self):
109 return "<Origin %s->%s<-%s (%s-%s)>" % (
110 self.code[:self.start],
111 self.code[self.start:self.end],
112 self.code[self.end:],
113 self.start, self.end)
115 # We reimplement patsy.util.no_pickling, to avoid circular import issues
116 def __getstate__(self):
117 raise NotImplementedError
119def test_Origin():
120 o1 = Origin("012345", 2, 4)
121 o2 = Origin("012345", 4, 5)
122 assert o1.caretize() == "012345\n ^^"
123 assert o2.caretize() == "012345\n ^"
124 o3 = Origin.combine([o1, o2])
125 assert o3.code == "012345"
126 assert o3.start == 2
127 assert o3.end == 5
128 assert o3.caretize(indent=2) == " 012345\n ^^^"
129 assert o3 == Origin("012345", 2, 5)
131 class ObjWithOrigin(object):
132 def __init__(self, origin=None):
133 self.origin = origin
134 o4 = Origin.combine([ObjWithOrigin(o1), ObjWithOrigin(), None])
135 assert o4 == o1
136 o5 = Origin.combine([ObjWithOrigin(o1), o2])
137 assert o5 == o3
139 assert Origin.combine([ObjWithOrigin(), ObjWithOrigin()]) is None
141 from patsy.util import assert_no_pickling
142 assert_no_pickling(Origin("", 0, 0))