Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AttributeError: 'tuple' object has no attribute 'model' #99

Closed
thuvh opened this issue Nov 24, 2023 · 8 comments
Closed

AttributeError: 'tuple' object has no attribute 'model' #99

thuvh opened this issue Nov 24, 2023 · 8 comments
Assignees

Comments

@thuvh
Copy link
Contributor

thuvh commented Nov 24, 2023

Hi, I found another exception

ERROR 2023-11-24 10:15:23,651 uds.REST.methods.services:deleteItem 223 Deleting service
Traceback (most recent call last):
  File "/home/thuvh/workspaces/opensource/openuds/server/src/uds/REST/methods/services.py", line 220, in deleteItem
    service.delete()
  File "/home/thuvh/.pyenv/versions/openuds_master_web/lib/python3.11/site-packages/django/db/models/base.py", line 1132, in delete
    return collector.delete()
           ^^^^^^^^^^^^^^^^^^
  File "/home/thuvh/.pyenv/versions/openuds_master_web/lib/python3.11/site-packages/django/db/models/deletion.py", line 463, in delete
    signals.pre_delete.send(
  File "/home/thuvh/.pyenv/versions/openuds_master_web/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 176, in send
    return [
           ^
  File "/home/thuvh/.pyenv/versions/openuds_master_web/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 177, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thuvh/workspaces/opensource/openuds/server/src/uds/models/service.py", line 227, in beforeDelete
    clean(toDelete)
  File "/home/thuvh/workspaces/opensource/openuds/server/src/uds/core/util/permissions.py", line 52, in clean
    models.Permissions.cleanPermissions(objtype.ObjectType.from_model(obj).type, obj.pk)
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thuvh/workspaces/opensource/openuds/server/src/uds/core/util/objtype.py", line 89, in from_model
    if objType.model == type(model):
       ^^^^^^^^^^^^^
  File "/home/thuvh/workspaces/opensource/openuds/server/src/uds/core/util/objtype.py", line 79, in model
    return self.value.model
           ^^^^^^^^^^^^^^^^
AttributeError: 'tuple' object has no attribute 'model'

I think it related to casting enum element here to ObjTypeInfo.

I fix by change from return self.value.model to return self.value[1] code, but i think it's not right with your design.

@dkmstr
Copy link
Collaborator

dkmstr commented Nov 24, 2023

Mmmm, this is strange, can you try this on your installation?
go to your uds folder and execute:
./manage.py shell
Now, try this:
In [1]: from uds.core.util import objtype

In [2]: from uds import models

In [3]: objtype.ObjectType.from_model(models.Service.objects.first())
Out[3]: <ObjectType.SERVICE: ObjTypeInfo(type=2, model=<class 'uds.models.service.Service'>)>

Should return and ObjTypeInfo object, not a tuple.

The used of [1] is fine, but the strange thing here is that it should be a named tuple...

As all members are ObjTypeInfo (if i didn't miss anything... :P)

class ObjectType(enum.Enum):
PROVIDER = ObjTypeInfo(1, models.Provider)
SERVICE = ObjTypeInfo(2, models.Service)
OSMANAGER = ObjTypeInfo(3, models.OSManager)
TRANSPORT = ObjTypeInfo(4, models.Transport)
NETWORK = ObjTypeInfo(5, models.Network)
POOL = ObjTypeInfo(6, models.ServicePool)
USER_SERVICE = ObjTypeInfo(7, models.UserService)
AUTHENTICATOR = ObjTypeInfo(8, models.Authenticator)
USER = ObjTypeInfo(9, models.User)
GROUP = ObjTypeInfo(10, models.Group)
STATS_COUNTER = ObjTypeInfo(11, models.StatsCounters)
STATS_EVENTS = ObjTypeInfo(12, models.StatsEvents)
CALENDAR = ObjTypeInfo(13, models.Calendar)
CALENDAR_RULE = ObjTypeInfo(14, models.CalendarRule)
METAPOOL = ObjTypeInfo(15, models.MetaPool)
ACCOUNT = ObjTypeInfo(16, models.Account)
# Actor and Tunnel tokens are now on REGISTERED_SERVER, so removed
MFA = ObjTypeInfo(19, models.MFA)
REGISTERED_SERVER = ObjTypeInfo(20, models.Server)
REGISTERED_SERVER_GROUP = ObjTypeInfo(21, models.ServerGroup)
ACCOUNT_USAGE = ObjTypeInfo(22, models.AccountUsage)
IMAGE = ObjTypeInfo(23, models.Image)
LOG = ObjTypeInfo(24, models.Log)
NOTIFICATION = ObjTypeInfo(25, models.Notification)
TICKET_STORE = ObjTypeInfo(26, models.TicketStore)

No tuple is used on the enum...
Anyway, the [1] is fine, in fact, it was that way previously, but changed to a more convenient and easier to understand namedtuple.

Let me know the results please.

@thuvh
Copy link
Contributor Author

thuvh commented Nov 24, 2023

yep, here is the output
pwd is /home/thuvh/workspaces/opensource/openuds/server/src

python manage.py shell
Python 3.11.1 (main, Oct 27 2023, 14:27:45) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.17.2 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from uds.core.util import objtype

In [2]: from uds import models

In [3]: objtype.ObjectType.from_model(models.Service.objects.first())
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[3], line 1
----> 1 objtype.ObjectType.from_model(models.Service.objects.first())

File /home/thuvh/workspaces/opensource/openuds/server/src/uds/core/util/objtype.py:89, in ObjectType.from_model(model)
     86 @staticmethod
     87 def from_model(model: 'Model') -> 'ObjectType':
     88     for objType in ObjectType:
---> 89         if objType.model == type(model):
     90             return objType
     91     raise ValueError(f'Invalid model type: {model}')

File /home/thuvh/workspaces/opensource/openuds/server/src/uds/core/util/objtype.py:79, in ObjectType.model(self)
     77 @property
     78 def model(self) -> typing.Type['Model']:
---> 79     return self.value.model

AttributeError: 'tuple' object has no attribute 'model'

In [4]: x = objtype.ObjectType.PROVIDER

In [5]: x.value
Out[5]: (1, uds.models.provider.Provider)

In [6]: x.model
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[6], line 1
----> 1 x.model

File /home/thuvh/workspaces/opensource/openuds/server/src/uds/core/util/objtype.py:79, in ObjectType.model(self)
     77 @property
     78 def model(self) -> typing.Type['Model']:
---> 79     return self.value.model

AttributeError: 'tuple' object has no attribute 'model'

In [7]:


@dkmstr
Copy link
Collaborator

dkmstr commented Nov 24, 2023

Can you paste your objtype.py file here please?
its very weird this :)

I have included a couple of changes, but only to add some more info, and a consisntence one:

Also, my output from your [4..]:

In [4]: objtype.ObjectType.PROVIDER
Out[4]: <ObjectType.PROVIDER: ObjTypeInfo(type=1, model=<class 'uds.models.provider.Provider'>)>

In [5]: objtype.ObjectType.PROVIDER.value
Out[5]: ObjTypeInfo(type=1, model=<class 'uds.models.provider.Provider'>)

In [6]: x = objtype.ObjectType.PROVIDER

In [7]: x.model
Out[7]: uds.models.provider.Provider

Weird :P

@thuvh
Copy link
Contributor Author

thuvh commented Nov 24, 2023

# -*- coding: utf-8 -*-

#
# Copyright (c) 2014-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright notice,
#      this list of conditions and the following disclaimer in the documentation
#      and/or other materials provided with the distribution.
#    * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
#      may be used to endorse or promote products derived from this software
#      without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import logging
import typing
import enum

from uds import models

# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
    from django.db.models import Model

logger = logging.getLogger(__name__)

class ObjTypeInfo(typing.NamedTuple):
    type: int
    model: typing.Type['Model']
    

class ObjectType(enum.Enum):
    PROVIDER = ObjTypeInfo(1, models.Provider)
    SERVICE = ObjTypeInfo(2, models.Service)
    OSMANAGER = ObjTypeInfo(3, models.OSManager)
    TRANSPORT = ObjTypeInfo(4, models.Transport)
    NETWORK = ObjTypeInfo(5, models.Network)
    POOL = ObjTypeInfo(6, models.ServicePool)
    USER_SERVICE = ObjTypeInfo(7, models.UserService)
    AUTHENTICATOR = ObjTypeInfo(8, models.Authenticator)
    USER = ObjTypeInfo(9, models.User)
    GROUP = ObjTypeInfo(10, models.Group)
    STATS_COUNTER = ObjTypeInfo(11, models.StatsCounters)
    STATS_EVENTS = ObjTypeInfo(12, models.StatsEvents)
    CALENDAR = ObjTypeInfo(13, models.Calendar)
    CALENDAR_RULE = ObjTypeInfo(14, models.CalendarRule)
    METAPOOL = ObjTypeInfo(15, models.MetaPool)
    ACCOUNT = ObjTypeInfo(16, models.Account)
    # Actor and Tunnel tokens are now on REGISTERED_SERVER, so removed
    MFA = ObjTypeInfo(19, models.MFA)
    REGISTERED_SERVER = ObjTypeInfo(20, models.Server)
    REGISTERED_SERVER_GROUP = ObjTypeInfo(21, models.ServerGroup)
    ACCOUNT_USAGE = ObjTypeInfo(22, models.AccountUsage)
    IMAGE = ObjTypeInfo(23, models.Image)
    LOG = ObjTypeInfo(24, models.Log)
    NOTIFICATION = ObjTypeInfo(25, models.Notification)
    TICKET_STORE = ObjTypeInfo(26, models.TicketStore)

    @property
    def model(self) -> typing.Type['Model']:
        return self.value.model

    @property
    def type(self) -> int:
        """Returns the integer value of this object type"""
        return self.value.type

    @staticmethod
    def from_model(model: 'Model') -> 'ObjectType':
        for objType in ObjectType:
            if objType.model == type(model):
                return objType
        raise ValueError(f'Invalid model type: {model}')

    def __eq__(self, __o: object) -> bool:
        """Compares with another ObjType, and includes int comparison

        Args:
            __o (object): Object to compare

        Returns:
            bool: True if equal, False otherwise
        """
        return super().__eq__(__o) or self.value[0] == __o

git log

commit efd78ddffb82d3fb2a537bc43c03e5e1fb0f8a04 (HEAD -> master, upstream/master, upstream/HEAD, origin/master)
Author: Adolfo Gómez García <dkmaster@dkmon.com>
Date:   Thu Nov 23 17:06:03 2023 +0100

    Added MFA permission

commit 5fab570b92c2a061e9b3167bafb452b20367ae61
Author: Adolfo Gómez García <dkmaster@dkmon.com>
Date:   Thu Nov 23 16:20:51 2023 +0100
    Fixed serialization of Hidden Fields on InitGui

git status

(openuds_master_web)  ✘  ~/workspaces/opensource/openuds/server/src   master ✚  git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   ../.env
        modified:   uds/REST/dispatcher.py
        modified:   uds/core/auths/authenticator.py
        modified:   uds/core/services/service.py
        modified:   uds/core/ui/user_interface.py
        modified:   uds/models/authenticator.py

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        ../compose.yaml
        openuds.db
        private.pem
        uds/management/commands/authenticator.py

all changes not related to objtype

@thuvh
Copy link
Contributor Author

thuvh commented Nov 24, 2023

more detail about my env:
os: ubuntu 22.04

python -c "import sys;print(sys.version)"
3.11.1 (main, Oct 27 2023, 14:27:45) [GCC 11.4.0]
(openuds_master_web)  ~/workspaces/opensource/openuds/server/src   master ✚  python -c "import sys;print('Python '+sys.version.replace('\n','')+' on '+sys.platform)"
Python 3.11.1 (main, Oct 27 2023, 14:27:45) [GCC 11.4.0] on linux

@dkmstr
Copy link
Collaborator

dkmstr commented Nov 24, 2023

It's fine, all variables are initialized to "ObjInfoType", not Tuples :S
I can change it to use [0] and [1], but i really don't understand WHY is different mine from yours (i use python 3.11.2, but it's the same with 3.9 for example :S)

can you try also this?

In [1]: import typing

In [2]: class Test(typing.NamedTuple):
...: a: int
...: b: str
...:

In [3]: Test(1, 'b')
Out[3]: Test(a=1, b='b')

@dkmstr
Copy link
Collaborator

dkmstr commented Nov 24, 2023

Seems the problem is with 3.11.1:
python/cpython#100098

The NamedTuple is converted to Tuples, so update to at least 3.11.2.

It's a python 3.11.1 bug :S (that was weird :P)

@thuvh
Copy link
Contributor Author

thuvh commented Nov 24, 2023

I worked! Thanks

@thuvh thuvh closed this as completed Nov 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants