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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions