PyWavefront
PyWavefront copied to clipboard
Wavefront class as entrypoint. Alternatives?
Some thoughts on this. I don't know much about the history of the library, so I might be off here. I suspect the decision to use Wavefront()
as the entrypoint was to make to VERY simple to use, something that I definitely support.
The way the Wavefront class is promoted as the entrypoint right now makes it a bit awkward to extend the parsers. The class is the result object and the entrypoint at the same time. Right now we really only have one good way to use a custom parser.
Wavefront.parser_cls = MyCustomParser
result = Wavefront(...)
Also, if my custom parser have additional parameters, the Wavefront constructor will need to forward that my class. We can solve that using **kwargs
, but it makes things a bit awkward.
Another weird way people could avoid Wavefront object, people can be creative doing this:
result = Wavefront(parse=False)
parser = ObjParser(result, "my.obj", ...)
.. but that will also create an internal parser in Wavefront, so you would have to extend the class to avoid that.
I personally think it would be cleaner if the parser was instantiated directly creating its own "result" object returned in parse()
.
parser = ObjParser("my.obj", ...)
result = parser.parse()
# And/or the result is available as an attribute
parser.result
Still I think preserving backwards compatibility is important here, so we should support both. We can create a base class ParserResult
only containing parse result attributes. Wavefront can extend this class adding the methods it has now.
class ParserResult(object):
def __init___(self):
self.file_name = file_name
self.mtllibs = []
self.materials = {}
self.meshes = {} # Name mapping
self.mesh_list = [] # Also includes anonymous meshes
class Wavefront(ParserResult):
"""Identical to the old class except we inherit attributes"""
def __init__(self, file_name, strict=False, ..., *kwargs):
... etc ...
Parsers will then have an optional attribute for the Wavefront/ParserResult. If not supplied it will create its own ParseResult instance and return it in parse
. In addition the parser class is configurable as a class variable.
class Parser(object):
"""The base parser"""
result_cls = ParserResult
def __init__(self, result_instance=None):
self.result = result_instance or result_cls()
def parse(self):
...
return self.result
This way we can support Wavefront passing in itself and instantiation of parsers directly. We should still promote the Wavefront()
approach as before, but combine this change with covering the more advanced uses of the library with sphinx docs #66
"Easy things easy" (simple Wavefront entry point) + "hard things possible" (Parser + ParserResult) sounds like a very good idea.