py-backwards
py-backwards copied to clipboard
Error triggering on this function but why?
The following code can't be converted from 3.6 to python 3.5 and the error seems to stem from something in py-backwards, I just can't figure out what. I tried it in the online demo and it blew up.
def validate_spec(self, file: h5py.File) -> bool:
"""
Validate the LoomConnection object against the format specification.
Args:
file: h5py File object
Returns:
True if the file conforms to the specs, else False
Remarks:
Upon return, the instance attributes 'self.errors' and 'self.warnings' contain
lists of errors and warnings, and the 'self.summary' attribute contains a summary
of the file contents.
"""
matrix_types = ["float16", "float32", "float64", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64"]
vertex_types = ["int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64"]
weight_types = ["float16", "float32", "float64"]
def delay_print(text: str) -> None:
self.summary.append(text)
def dt(t: str) -> str:
if str(t).startswith("|S"):
return f"string"
return str(t)
width_ra = max([len(x) for x in (file["row_attrs"].keys())])
width_ca = max([len(x) for x in (file["col_attrs"].keys())])
width_globals = max([len(x) for x in file.attrs.keys()])
width_layers = 0
if "layers" in file and len(file["layers"]) > 0:
width_layers = max([len(x) for x in file["layers"].keys()])
width_layers = max(width_layers, len("Main matrix"))
width = max(width_ca, width_ra, width_globals)
delay_print("Global attributes:")
for key, value in file.attrs.items():
if type(value) is str:
self.warnings.append(f"Global attribute '{key}' has dtype string, which will be deprecated in future Loom versions")
delay_print(f"{key: >{width}} string")
else:
delay_print(f"{key: >{width}} {dt(file.attrs[key].dtype)}")
return len(self.errors) == 0
If I remove the following code it works,
delay_print("Global attributes:")
for key, value in file.attrs.items():
if type(value) is str:
self.warnings.append(f"Global attribute '{key}' has dtype string, which will be deprecated in future Loom versions")
delay_print(f"{key: >{width}} string")
else:
delay_print(f"{key: >{width}} {dt(file.attrs[key].dtype)}")
bit this peace breaks something. Without triggering a syntax error. The code comes from this project and file: https://github.com/linnarsson-lab/loompy/blob/master/loompy/loom_validator.py
What is going on here?
To simplify this somewhat.
The error seems to trigger on a type of line formated as this:
f"{key: >{width}} string"
I think the issue is that py-backwards converts
f"Hello {key: >10} {x: >5} world"
into
''.join(['Hello ', '{: >10}'.format(key), ' ', '{: >5}'.format(x), ' world'])
So, basically treating each { }
fragment separately, and then joining together all the pieces. With a naïve search for pairs of curly braces, I can see how this will fail when curly braces are nested.
A possible solution would be to convert instead to:
"Hello {key: >10} {x: >5} world".format(**{**locals(), **globals()})
which simply requires finding all f-strings, removing the f, and appending an expression that is always the same. I think that's likely to always work, but maybe there's a performance hit (though I suspect the actual string formatting is slower than merging two dictionaries).
I tested the specific case above, and the following two expressions both work and give equivalent results:
f"{key: >{width}} string" # Python 3.6
"{key: >{width}} string".format(**{**locals(), **globals()}) # Converted to Python 3.5
Oh, I realize you need to put the actual expressions in the arguments (since f-strings allow expressions inside the curly braces). So it would have to be something like:
f"{key: >{width + 2}} string" # Python 3.6
"{a: >{b}} string".format(a=key, b=width + 2) # Converted to Python 3.5
which means you'll need to parse the nested curly braces anyway.