Closed lycantropos closed 6 years ago
(valid for version 0.0.2
)
For now if someone wants (for example) change strategy for generating values for String
columns: it can be manually done by modification of factories_by_sql_types
object in records
module like
from hypothesis_sqlalchemy import records
from sqlalchemy.sql.sqltypes import String
...
# defining `custom_factory`
...
records.factories_by_sql_types[String] = custom_factory
...
# generating values with `custom_factory` for `String` columns
...
or more specific: let's say we want to generate IP addresses using regular expression from here
('\A(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\Z')
(more about \A
, \Z
at docs)
We can use hypothesis.strategies.from_regex factory like
import re
import textwrap
from functools import partial
from typing import Pattern
from hypothesis import strategies
from sqlalchemy.sql.sqltypes import String
from hypothesis_sqlalchemy import records
from hypothesis_sqlalchemy.types import Strategy
def string_type_values_factory_by_pattern(string_type: String,
*,
pattern: Pattern) -> Strategy:
result = strategies.from_regex(pattern)
max_length = string_type.length
if max_length is not None:
cropper = partial(textwrap.shorten,
width=max_length,
placeholder='')
result = result.map(cropper)
return result
...
ip_pattern = re.compile('\A(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\Z')
records.factories_by_sql_types[String] = partial(
string_type_values_factory_by_pattern,
pattern=ip_pattern)
...
# generating values with `custom_factory` for `String` columns
...
Same goes to changing strategy for generating values by python types.
In 0.0.3
I've replaced factories_by_sql_types
mapping with functools.singledispatch
decorated records.from_column_type
function, so changing behavior for all String
column types can be done like
import re
import textwrap
from functools import partial
from typing import Pattern
from hypothesis import strategies
from sqlalchemy import String
from hypothesis_sqlalchemy import records
from hypothesis_sqlalchemy.types import Strategy
def string_type_values_factory_by_pattern(string_type: String,
*,
pattern: Pattern) -> Strategy:
result = strategies.from_regex(pattern)
max_length = string_type.length
if max_length is not None:
cropper = partial(textwrap.shorten,
width=max_length,
placeholder='')
result = result.map(cropper)
return result
...
ip_pattern = re.compile('\A(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.'
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\Z')
records.from_column_type.register(String)(partial(
string_type_values_factory_by_pattern,
pattern=ip_pattern))
To make package more flexible we need to provide simple and comfortable way of changing modules context (like global variables and functions).