Problem when instantiating sub-class of owl.Thing

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Problem when instantiating sub-class of owl.Thing

cracoras
This post was updated on .
I am running into the following issue when using owlready2 to define and instantiate my ontology classes.
- The scenario is that I have 1 class that is defined at run time, that inherits from a class that is defined based on a type: MasterData or Transaction, which on its turn, inherits from the BusinessDocument.
- The BusinessDocument class needs to inherit from the Thing module that is imported in my package.

The following code has been implemented to create all the classes in the chain:


from owlready2 import *

with onto:
    class BusinessDocument(Thing):
        @staticmethod
        def get_class(doc_type):
            switch = {
                'MasterData': MasterData,
                'Transactional': Transactional
            }
            cls = switch.get(doc_type, lambda: "Invalid Noun Type")
            return cls
       
        def __init__(self, doc_id, location, doc_type, color, size):
            self.doc_id = doc_id
            self.location = location
            self.doc_type = doc_type
            self.color = color
            self.size = size
   
        @property
        def get_location(self):
            return self.location
   
        @property
        def get_doc_id(self):
            return self.doc_id

with onto:
    class MasterData(BusinessDocument):
        def __init__(self, doc_id, location, color, size):
            BusinessDocument.__init__(self, doc_id, location, color, size, 'MasterData')

with onto:    
    class Transactional(BusinessDocument):
        def __init__(self, doc_id, location, color, size):
            BusinessDocument.__init__(self, doc_id, location, color, size, 'Transactional')


with onto:
    class NounClass():
        @staticmethod
        def get_class(doc_name, doc_type):
            return type(doc_name, (BusinessDocument.get_class(doc_type),
                               BusinessDocument, ),dict.fromkeys(['doc_id', 'location', 'color', 'size',]))


At run time when I get the doc_name and I get a new class, but when I try to instantiate.

```
invoice_cls = NounClass.get_class('Invoice', 'Transactional')
my_invoice = invoice_cls('IN-1234', 'New York', 'blue', 'big')
```

Calls to the type() and __mro__() methods for the invoice_cls gives me the following information:


DEBUG:app.__main__:Type of from_noun is [(bod_ontology.Invoice, bod_ontology.MasterData, bod_ontology.BusinessDocument, owl.Thing, <class 'object'>)]
DEBUG:app.__main__:Class type is [<class 'owlready2.entity.ThingClass'>]

But then I get an exception thrown related to the `__new__()` method:

DEBUG:app.__main__:Type of from_noun is [(bod_ontology.BillFromPartyMaster, bod_ontology.MasterData, bod_ontology.BusinessObjectDocument, owl.Thing, <class 'object'>)]
DEBUG:app.__main__:Class type is [<class 'owlready2.entity.ThingClass'>]
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 167, in parse_xml
    my_invoice = invoice_cls('IN-1234', 'New York', 'blue', 'big')
--- Logging error ---
Traceback (most recent call last):
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 167, in parse_xml
    my_invoice = invoice_cls('IN-1234', 'New York', 'blue', 'big')
TypeError: __new__() takes from 1 to 3 positional arguments but 5 were given

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/cracoras/.pyenv/versions/3.8.1/lib/python3.8/logging/__init__.py", line 1081, in emit
    msg = self.format(record)
  File "/Users/cracoras/.pyenv/versions/3.8.1/lib/python3.8/logging/__init__.py", line 925, in format
    return fmt.format(record)
  File "/Users/cracoras/.pyenv/versions/3.8.1/lib/python3.8/logging/__init__.py", line 664, in format
    record.message = record.getMessage()
  File "/Users/cracoras/.pyenv/versions/3.8.1/lib/python3.8/logging/__init__.py", line 369, in getMessage
    msg = msg % self.args
TypeError: must be real number, not TypeError
Call stack:
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1758, in <module>
    main()
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1752, in main
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1147, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 520, in <module>
    exit(main())
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 515, in main
    query_database()
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 103, in query_database
    csv_record = parse_xml(bod_type, date_time, from_lid, tenant, xml_string)
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 174, in parse_xml
    log.debug("Got an error [%e]", e)
Arguments: (TypeError('__new__() takes from 1 to 3 positional arguments but 5 were given'),)

```

Then if I try to make the instantiation call without the arguments, I get a different exception related to the `__init__()` method, and here is the full :

```
DEBUG:app.__main__:Type of from_noun is [(bod_ontology.BillFromPartyMaster, bod_ontology.MasterData, bod_ontology.BusinessObjectDocument, owl.Thing, <class 'object'>)]
DEBUG:app.__main__:Class type is [<class 'owlready2.entity.ThingClass'>]
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 167, in parse_xml
    my_invoice = invoice_cls()
--- Logging error ---
Traceback (most recent call last):
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 167, in parse_xml
    my_obj = MyCls()
TypeError: __init__() missing 4 required positional arguments: 'doc_id', 'location', 'color', and 'size'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/cracoras/.pyenv/versions/3.8.1/lib/python3.8/logging/__init__.py", line 1081, in emit
    msg = self.format(record)
  File "/Users/cracoras/.pyenv/versions/3.8.1/lib/python3.8/logging/__init__.py", line 925, in format
    return fmt.format(record)
  File "/Users/cracoras/.pyenv/versions/3.8.1/lib/python3.8/logging/__init__.py", line 664, in format
    record.message = record.getMessage()
  File "/Users/cracoras/.pyenv/versions/3.8.1/lib/python3.8/logging/__init__.py", line 369, in getMessage
    msg = msg % self.args
TypeError: must be real number, not TypeError
Call stack:
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1758, in <module>
    main()
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1752, in main
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1147, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 520, in <module>
    exit(main())
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 515, in main
    query_database()
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 103, in query_database
    csv_record = parse_xml(bod_type, date_time, from_lid, tenant, xml_string)
  File "/Users/cracoras/PycharmProjects/bod2owl/app/convert_data.py", line 174, in parse_xml
    log.debug("Got an error [%e]", e)
Arguments: (TypeError("__init__() missing 4 required positional arguments: 'doc_id', 'location', 'color', and 'size'"),)

```

This only happens when I inherit from the 'Thing' class. If I remove the inheritance the code runs just fine.
It looks like I am messing up with the instantiation sequence in the class hierarchy.

I tried also using the types() function in the format NewClass = types.new_class(noun, (BusinessObjectDocument.get_class(noun_type), BusinessObjectDocument, )) and at the end I get the same errors.
Reply | Threaded
Open this post in threaded view
|

Re: Problem when instantiating sub-class of owl.Thing

Jiba
Administrator
Hi,

Owlready uses the __new__() special method for initializing object. The default __new__() handles keyword arguments, but not non-keyword arguments as you are using.

There are 2 solutions :

1) you can use keyword arguments, for example:

my_invoice = invoice_cls(doc_id = 'IN-1234', location = 'New York', color = 'blue', size = 'big')

2) you can override __new__(), as following:

    class BusinessDocument(Thing):
        def __new__(Class, doc_id, location, doc_type, color, size):
            Thing.__new__(Class)

        def __init__(self, doc_id, location, doc_type, color, size):
            self.doc_id = doc_id
            self.location = location
            self.doc_type = doc_type
            self.color = color
            self.size = size

And the same for the other classes.

Jiba
Reply | Threaded
Open this post in threaded view
|

Re: Problem when instantiating sub-class of owl.Thing

cracoras
This post was updated on .
Hi Jiba.

I figured out the 1st solution and was able to move forward it, but I like the second one better as it makes the instantiation of the subclasses simpler.

So I made the following changes in my code.


from owlready2 import *

onto = get_ontology('http://test.org/onto.owl')

with onto:
    class BusinessDocument(Thing):
        @staticmethod
        def get_class(doc_type):
            switch = {
                'MasterData': MasterData,
                'Transactional': Transactional
            }
            cls = switch.get(doc_type, lambda: "Invalid Noun Type")
            return cls
       
        def __new__ (cls, doc_id, location, color, size, doc_type):
            Thing.__new__(cls)
       
        def __init__ (self, doc_id, location, color, size, doc_type):
            self.doc_id = doc_id,
            self.location = location
            self.color = color
            self.size = size
            self.doc_type = doc_type
           
        def get_type(self):
            return self.doc_type

           
    class MasterData(BusinessDocument):  
       
        def __new__ (cls, doc_id, location, color, size):
            BusinessDocument.__new__(cls, doc_id, location, color, size, 'MasterData')
       
        def __init__ (self, doc_id, location, color, size):
            BusinessDocument.__init__(doc_id, location, color, size, 'MasterData')

    class Transactional(BusinessDocument):
       
        def __new__ (cls, doc_id, location, color, size):
            BusinessDocument.__new__(cls, doc_id, location, color, size, 'Transactional')

        def __init__ (self, doc_id, location, color, size):
            BusinessDocument.__init__(doc_id, location, color, size, 'Transactional')


    class NounClass():
        @staticmethod
        def get_class(doc_name, doc_type):
            return type(doc_name, (BusinessDocument.get_class(doc_type), ),dict())

        def __new__ (cls, doc_id, location, color, size):
            super().__new__(cls, doc_id, location, color, size)

       
        def __init__(self, doc_id, location, color, size):
            super().__init__(doc_id, location, color, size)


I am able to successfully get the new class type in the following line:

invoice_class = NounClass.get_class('Invoice', 'Transactional')

When I print the mro for it I get what seems to be the correct inheritance chain.

print(invoice_class.__mro__)

(onto.Invoice, onto.Transactional, onto.BusinessDocument, owl.Thing, <class 'object'>)


Then I try to create the new invoice object:
my_obj = invoice_class('IN-001', 'London', "Red", "Small")

But it sees like that object is None, but I get the following error when I call the type or __mro__ property for it.
print(my_obj.__mro__)

AttributeError                            Traceback (most recent call last)
<ipython-input-80-e4b41e54bf70> in <module>
     60 print(invoice_class.__mro__)
     61 my_obj = invoice_class('IN-001', 'London', "Red", "Small")
---> 62 print(my_obj.__mro__)
     63

AttributeError: 'NoneType' object has no attribute '__mro__'

Thank you in advance for any help.


MD.