Skip to content

Null FreeText fields break Table.dict() method #22

@psadil

Description

@psadil

Some columns of type FreeText can be nullable, as in

fmricuffnotes = Column(FreeText, nullable=True, comments=None)

But when retrieving a record that has a null FreeText field, the record's .dict() fails with an error like

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File /Users/psadil/git/a2cps/imaging-handler/test.py:1
----> 1 existing_data = vbr_records[0].dict()

File ~/git/a2cps/imaging-handler/.venv/lib/python3.12/site-packages/vbr/pgrest/table.py:81, in Table.dict(self)
     78 _d = getattr(self, v, None)
     80 # Cast to proper Python type
---> 81 d = self.__class_attrs__[v].ctype.cast(_d)
     83 # Do not populate dict with None values where attribute is a primary key
     84 # Do not populate dict with None values if attribute is nullable
     85 nullable = self.__class_attrs__[v].nullable

File ~/git/a2cps/imaging-handler/.venv/lib/python3.12/site-packages/vbr/pgrest/types.py:145, in FreeText.cast(cls, value)
    143 """Formats a long string value into a sanitized and escaped single line"""
    144 value = super().cast(value)
--> 145 value = re.sub(r"\s+", " ", value, flags=re.MULTILINE)
    146 # HACK This is to work around annoying edge case in json
    147 # specs that differ materially on if and how
    148 # single-quote characters must be escaped.
    149 value = re.sub(r"'", "", value, flags=re.MULTILINE)

File ~/.local/share/uv/python/cpython-3.12.5-macos-aarch64-none/lib/python3.12/re/__init__.py:186, in sub(pattern, repl, string, count, flags)
    179 def sub(pattern, repl, string, count=0, flags=0):
    180     """Return the string obtained by replacing the leftmost
    181     non-overlapping occurrences of the pattern in string by the
    182     replacement repl.  repl can be either a string or a callable;
    183     if a string, backslash escapes in it are processed.  If it is
    184     a callable, it's passed the Match object and must return
    185     a replacement string to be used."""
--> 186     return _compile(pattern, flags).sub(repl, string, count)

TypeError: expected string or bytes-like object, got 'NoneType'

The issue stems from re.sub failing when it tries to convert a value that is None

value = re.sub(r"\s+", " ", value, flags=re.MULTILINE)

For example

>>> import re
>>> re.sub("from", "to", None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/psadil/mambaforge/lib/python3.11/re/__init__.py", line 185, in sub
    return _compile(pattern, flags).sub(repl, string, count)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: expected string or bytes-like object, got 'NoneType'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions