@@ -24,11 +24,16 @@ class BaseFuzzyAttribute(declarations.BaseDeclaration):
2424 Custom fuzzers should override the `fuzz()` method.
2525 """
2626
27- def fuzz (self ): # pragma: no cover
27+ def _resolve (self , value , instance , step ):
28+ if isinstance (value , declarations .BaseDeclaration ):
29+ return value .evaluate_pre (instance = instance , step = step , overrides = {})
30+ return value
31+
32+ def fuzz (self , instance , step ):
2833 raise NotImplementedError ()
2934
3035 def evaluate (self , instance , step , extra ):
31- return self .fuzz ()
36+ return self .fuzz (instance , step )
3237
3338
3439class FuzzyAttribute (BaseFuzzyAttribute ):
@@ -43,7 +48,7 @@ def __init__(self, fuzzer):
4348 super ().__init__ ()
4449 self .fuzzer = fuzzer
4550
46- def fuzz (self ):
51+ def fuzz (self , instance , step ):
4752 return self .fuzzer ()
4853
4954
@@ -71,7 +76,7 @@ def __init__(self, prefix='', length=12, suffix='', chars=string.ascii_letters):
7176 self .length = length
7277 self .chars = tuple (chars ) # Unroll iterators
7378
74- def fuzz (self ):
79+ def fuzz (self , instance , step ):
7580 chars = [random .randgen .choice (self .chars ) for _i in range (self .length )]
7681 return self .prefix + '' .join (chars ) + self .suffix
7782
@@ -91,9 +96,10 @@ def __init__(self, choices, getter=None):
9196 self .getter = getter
9297 super ().__init__ ()
9398
94- def fuzz (self ):
99+ def fuzz (self , instance , step ):
95100 if self .choices is None :
96- self .choices = list (self .choices_generator )
101+ resolved = self ._resolve (self .choices_generator , instance , step )
102+ self .choices = list (resolved )
97103 value = random .randgen .choice (self .choices )
98104 if self .getter is None :
99105 return value
@@ -104,55 +110,55 @@ class FuzzyInteger(BaseFuzzyAttribute):
104110 """Random integer within a given range."""
105111
106112 def __init__ (self , low , high = None , step = 1 ):
107- if high is None :
108- high = low
109- low = 0
110-
111113 self .low = low
112114 self .high = high
113115 self .step = step
114-
115116 super ().__init__ ()
116117
117- def fuzz (self ):
118- return random .randgen .randrange (self .low , self .high + 1 , self .step )
118+ def fuzz (self , instance , step ):
119+ low = self ._resolve (self .low , instance , step )
120+ high = self ._resolve (self .high , instance , step )
121+ if high is None :
122+ high = low
123+ low = 0
124+ return random .randgen .randrange (low , high + 1 , self .step )
119125
120126
121127class FuzzyDecimal (BaseFuzzyAttribute ):
122128 """Random decimal within a given range."""
123129
124130 def __init__ (self , low , high = None , precision = 2 ):
125- if high is None :
126- high = low
127- low = 0.0
128-
129131 self .low = low
130132 self .high = high
131133 self .precision = precision
132-
133134 super ().__init__ ()
134135
135- def fuzz (self ):
136- base = decimal .Decimal (str (random .randgen .uniform (self .low , self .high )))
136+ def fuzz (self , instance , step ):
137+ low = self ._resolve (self .low , instance , step )
138+ high = self ._resolve (self .high , instance , step )
139+ if high is None :
140+ high = low
141+ low = 0.0
142+ base = decimal .Decimal (str (random .randgen .uniform (low , high )))
137143 return base .quantize (decimal .Decimal (10 ) ** - self .precision )
138144
139145
140146class FuzzyFloat (BaseFuzzyAttribute ):
141147 """Random float within a given range."""
142148
143149 def __init__ (self , low , high = None , precision = 15 ):
144- if high is None :
145- high = low
146- low = 0
147-
148150 self .low = low
149151 self .high = high
150152 self .precision = precision
151-
152153 super ().__init__ ()
153154
154- def fuzz (self ):
155- base = random .randgen .uniform (self .low , self .high )
155+ def fuzz (self , instance , step ):
156+ low = self ._resolve (self .low , instance , step )
157+ high = self ._resolve (self .high , instance , step )
158+ if high is None :
159+ high = low
160+ low = 0
161+ base = random .randgen .uniform (low , high )
156162 return float (format (base , '.%dg' % self .precision ))
157163
158164
@@ -161,22 +167,40 @@ class FuzzyDate(BaseFuzzyAttribute):
161167
162168 def __init__ (self , start_date , end_date = None ):
163169 super ().__init__ ()
164- if end_date is None :
165- if random .randgen .state_set :
166- cls_name = self .__class__ .__name__
167- warnings .warn (random_seed_warning .format (cls_name ), stacklevel = 2 )
168- end_date = datetime .date .today ()
169-
170- if start_date > end_date :
171- raise ValueError (
172- "FuzzyDate boundaries should have start <= end; got %r > %r."
173- % (start_date , end_date ))
174-
175- self .start_date = start_date .toordinal ()
176- self .end_date = end_date .toordinal ()
177-
178- def fuzz (self ):
179- return datetime .date .fromordinal (random .randgen .randint (self .start_date , self .end_date ))
170+ self ._start_is_decl = isinstance (start_date , declarations .BaseDeclaration )
171+ self ._end_is_decl = isinstance (end_date , declarations .BaseDeclaration )
172+ self .start_date = start_date
173+ self .end_date = end_date
174+ if not self ._start_is_decl and not self ._end_is_decl :
175+ if end_date is None :
176+ if random .randgen .state_set :
177+ cls_name = self .__class__ .__name__
178+ warnings .warn (random_seed_warning .format (cls_name ), stacklevel = 2 )
179+ end_date = datetime .date .today ()
180+ self .end_date = end_date
181+ if start_date > end_date :
182+ raise ValueError (
183+ "FuzzyDate boundaries should have start <= end; got %r > %r."
184+ % (start_date , end_date ))
185+ self ._start_ord = start_date .toordinal ()
186+ self ._end_ord = end_date .toordinal ()
187+
188+ def fuzz (self , instance , step ):
189+ if self ._start_is_decl or self ._end_is_decl :
190+ start_date = self ._resolve (self .start_date , instance , step )
191+ end_date = self ._resolve (self .end_date , instance , step )
192+ if end_date is None :
193+ end_date = datetime .date .today ()
194+ if start_date > end_date :
195+ raise ValueError (
196+ "FuzzyDate boundaries should have start <= end; got %r > %r."
197+ % (start_date , end_date ))
198+ start_ord = start_date .toordinal ()
199+ end_ord = end_date .toordinal ()
200+ else :
201+ start_ord = self ._start_ord
202+ end_ord = self ._end_ord
203+ return datetime .date .fromordinal (random .randgen .randint (start_ord , end_ord ))
180204
181205
182206class BaseFuzzyDateTime (BaseFuzzyAttribute ):
@@ -199,15 +223,8 @@ def __init__(self, start_dt, end_dt=None,
199223 force_hour = None , force_minute = None , force_second = None ,
200224 force_microsecond = None ):
201225 super ().__init__ ()
202-
203- if end_dt is None :
204- if random .randgen .state_set :
205- cls_name = self .__class__ .__name__
206- warnings .warn (random_seed_warning .format (cls_name ), stacklevel = 2 )
207- end_dt = self ._now ()
208-
209- self ._check_bounds (start_dt , end_dt )
210-
226+ self ._start_is_decl = isinstance (start_dt , declarations .BaseDeclaration )
227+ self ._end_is_decl = isinstance (end_dt , declarations .BaseDeclaration )
211228 self .start_dt = start_dt
212229 self .end_dt = end_dt
213230 self .force_year = force_year
@@ -217,13 +234,31 @@ def __init__(self, start_dt, end_dt=None,
217234 self .force_minute = force_minute
218235 self .force_second = force_second
219236 self .force_microsecond = force_microsecond
220-
221- def fuzz (self ):
222- delta = self .end_dt - self .start_dt
237+ if not self ._start_is_decl and not self ._end_is_decl :
238+ if end_dt is None :
239+ if random .randgen .state_set :
240+ cls_name = self .__class__ .__name__
241+ warnings .warn (random_seed_warning .format (cls_name ), stacklevel = 2 )
242+ end_dt = self ._now ()
243+ self .end_dt = end_dt
244+ self ._check_bounds (start_dt , end_dt )
245+
246+ def fuzz (self , instance , step ):
247+ if self ._start_is_decl or self ._end_is_decl :
248+ start_dt = self ._resolve (self .start_dt , instance , step )
249+ end_dt = self ._resolve (self .end_dt , instance , step )
250+ if end_dt is None :
251+ end_dt = self ._now ()
252+ self ._check_bounds (start_dt , end_dt )
253+ else :
254+ start_dt = self .start_dt
255+ end_dt = self .end_dt
256+
257+ delta = end_dt - start_dt
223258 microseconds = delta .microseconds + 1000000 * (delta .seconds + (delta .days * 86400 ))
224259
225260 offset = random .randgen .randint (0 , microseconds )
226- result = self . start_dt + datetime .timedelta (microseconds = offset )
261+ result = start_dt + datetime .timedelta (microseconds = offset )
227262
228263 if self .force_year is not None :
229264 result = result .replace (year = self .force_year )
0 commit comments