Skip to content

Reimplmented parse_anything.py - #223

Closed
itsyongan wants to merge 3 commits into
spdx:mainfrom
itsyongan:main
Closed

itsyongan wants to merge 3 commits into
spdx:mainfrom
itsyongan:main

Conversation

@itsyongan

Copy link
Copy Markdown

No description provided.

@licquia licquia mentioned this pull request Aug 11, 2022
@licquia
licquia marked this pull request as ready for review August 11, 2022 20:29
from spdx.parsers import parse_anything


dirname = os.path.join(os.path.dirname(__file__), "data", "formats")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of replacing the old tests with the new ones, can we do both sets of tests?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Jeff, I added the formats test set back into test_parse_anything. Please tell me if there any other changes you would like me to make.

Signed-off-by: Josh Lin <linynjosh@gmail.com>
Signed-off-by: Josh Lin <linynjosh@gmail.com>
Signed-off-by: Josh Lin <linynjosh@gmail.com>
@itsyongan
itsyongan requested a review from licquia August 14, 2022 08:43
@nicoweidner

Copy link
Copy Markdown
Collaborator

@linynjosh Does this PR fix a specific issue? If it doesn't, can you add a general description of the purpose of the change to the PR?

@nicoweidner nicoweidner left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only understood the purpose of this change (at least what I now believe is the purpose) once I arrived at the test cases - a general description would have been really helpful!

What I now believe is the purpose of this PR: To allow parsing any file correctly, regardless of the file ending. That is, a json document should be parsed correctly, even if the user erroneously named it "document.rdf".

I am skeptical whether this is actually worth going for. While it technically adds some functionality and I don't see any hard reasons against it (at the moment!), I am in favor of failing early if the user submits a file with wrong ending. That is a user error, after all, and it will keep the tool simpler if we just attempt to parse once with the appropriate parser instead of applying "magic". As the tool grows more complex, there may be other issues arising from auto-detection (for example, format-specific options would become a bit weird to handle).

Finally, if we would auto-detect the input format, the CLI tool parameters would have to be changed as well, since the --from parameter would become redundant. For reference, the java tools also respect the specified input format and don't attempt to parse the input file with any parser available (this is the method it boils down to).

Opinions from other contributors would be great! (@licquia?)

Comment on lines +25 to +44
buildermodules = [rdfbuilders, jsonyamlxmlbuilders, jsonyamlxmlbuilders, jsonyamlxmlbuilders, tagvaluebuilders]
parsing_modules = [rdf, xmlparser, yamlparser, jsonparser, tagvalue]
read_datas = [False, False, False, False, True]
for i in range(len(buildermodules)):
parsing_module = parsing_modules[i]
buildermodule = buildermodules[i]
read_data = read_datas[i]
try:
p = parsing_module.Parser(buildermodule.Builder(), StandardLogger())
if hasattr(p, "build"):
p.build()
with open(fn) as f:
if read_data:
data = f.read()
return p.parse(data)
else:
return p.parse(f)
except:
if i == len(buildermodules) - 1:
raise FileTypeError("FileType Not Supported" + str(fn))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, you are trying to:

  • loop over all the available builder/parsing modules
  • let each one try to parse
  • catch any exceptions, and if the last one throws an exception, decide that it's an unsupported file type

Tbh, I don't like this approach at all. Exceptions should not be used for control flow, and this also creates a lot of unnecessary calls. There are also three arrays buildermodules, parsing_modules and read_datas which are linked in a fairly weak way, so if some developer makes a change to, say, buildermodules in the future, they could easily get out of sync.

I would prefer a different approach that is closer to the previous version, but a bit more structured:

  • Create an enum containing all the supported file types
  • Create a helper class (maybe FileTypeModules?) containing parsing_module, builder_module and read_data fields. You could use @dataclass to avoid writing default constructors, see e.g. https://stackoverflow.com/questions/48254562/python-equivalent-of-typescript-interface
  • Create a dictionary with the enum values as key and an appropriate instance of the FileTypeModules class as value (this can be at the top level since it is a static dictionary)
  • After getting the file ending, just look up the key in the dictionary to get the correct modules. If the key is not found, go to FileTypeError

What do you think?

@@ -0,0 +1,223 @@
{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About all these new test files:

  • As far as I can tell, they are simply duplicated from the existing test files under tests/data/formats
  • The different endings appear to be artificial (presumably to test whether a wrongly labelled file will still be parsed correctly)

Thoughts on this:

  • I would like to reduce duplication. For the original (correctly labelled) test files, we could use the existing ones. For the wrongly labelled ones, it would require some "before all tests" setup (creating the additional files with other endings) and "after all tests" cleanup (deleting the created files). From some quick googling, I think something with pytest fixtures should work, but I don't have experience with that myself. I'd also be fine with leaving this as a future improvement
  • After some deliberation, I don't think the approach of trying to parse a file with all parsers is worth going for. I will comment on this separately, since it concerns all of this PR

Comment on lines +32 to +33
dirname = os.path.join(os.path.dirname(__file__), "data", "formats")
test_files = [os.path.join(dirname, fn) for fn in os.listdir(dirname)]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like overwriting the existing variables here, can you create new ones for the second test suite?

On that note, though: It seems like the old tests are actually included in the new ones. In that case, there does not seem to be a point in repeating the old cases yet another time.

cc @licquia since you requested the old ones to be re-added

Comment on lines +42 to +44
except:
if i == len(buildermodules) - 1:
raise FileTypeError("FileType Not Supported" + str(fn))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more comment about this except: block: It will hide any exceptions that may be thrown by the parsers. This does not include validation errors (since those do not actually raise exceptions), but if anything goes wrong for other reasons, it won't be visible.

@armintaenzertng

Copy link
Copy Markdown
Collaborator

@linynjosh, do you plan to address this PR again?

@nicoweidner

Copy link
Copy Markdown
Collaborator

Closing this due to inactivity. Please ping me in case it should be reopened.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants