0

Considering below case,

ItemList Class:

# filename: item_list.py

# locators dictionary is intentionally placed outside `ItemList` class
locators = {
    'item_id': 'css=tr:nth-of-type({item_num}) .id',
    'item_title': 'css=tr:nth-of-type({item_num}) .alert-rules',
    # other properties
}

class ItemList(Item):
    # --- these are data descriptors ---
    # getting item_id value based on its item_num
    item_id = TextReadOnly(locators['item_id'].format(item_num=self.item_num))

    # getting item_title value based on its item_num
    item_title = TextReadOnly(locators['item_title'].format(item_num=self.item_num))


    def __init__(self, context, item_num=0):
        # self.item_num can't be passed to locators
        self.item_num = item_num

        super(ItemList, self).__init__(
            context
        )

In this case, I want to pass self.item_num value to item_num inside locators dictionary in ItemList class.

The reason is I want each of item_id and item_title refers to particular item_num.

I'm stuck on this part :

item_id = TextReadOnly(locators['item_id'].format(item_num=self.item_num))
item_title = TextReadOnly(locators['item_title'].format(item_num=self.item_num))

self.item_num value can't be passed to locators when instance is already made.

Implementation:

# filename: test.py

# print item_id values from item_num=1
item_1 = ItemList(context, 1)
print (‘Item_id_1: ’ + item_1.id)

# print item_id values from item_num=2
item_2 = ItemList(context, 2)
print (‘Item_id_2: ’ + item_2.id)

# ^ no result produced from item_1.id and item_2.id because self.item_num value can't be passed to locators

Is it possible to update data descriptor from instance variable?

How to pass instance variable value to data descriptor parameter?


Additional info:

I tried to call item_id and item_title as instance attribute but no luck. I noticed that data descriptor can’t be instance attribute from this post: Data descriptor should be a class attribute

Item Class:

# filename: item.py

import abc


class Item(Page):
    __metaclass__ = abc.ABCMeta

    def __init__(self, context):
        self.__context = context
        super(Item, self).__init__(
            self.__context.browser
        )

Descriptor:

# filename: text_read_only.py

class TextReadOnly(object):
    def __init__(self, locator):
        self.__locator = locator

    def __get__(self, instance, owner=None):
        try:
            e = instance.find_element_by_locator(self.__locator)
        except AttributeError:
            e = instance.browser.find_element_by_locator(self.__locator)

        return e.text

    def __set__(self, instance, value):
        pass
4
  • 1
    I think you are doing it wrong - the class attributes can't depend on instance attributes, as the class is initialized first. If you want item_id and item_title to vary per instance, why make them class attributes in the first place? Commented Aug 31, 2017 at 9:12
  • Hello @domoarrigato: I make item_id and item_title as class attributes because I want to use descriptor TextReadOnly. I noticed from this post that data descriptor should be a class attribute. Is there another approach to implement it? Commented Aug 31, 2017 at 9:17
  • 1
    If you want to keep them as class attributes, you can try to make them as "protected class properties", see [this][1] question to see how. In your specific case I think this is overkill, item_id and item_title look more like Instance Descriptors than Data Descriptors, so I'll make them as instance attributes. [1]: stackoverflow.com/questions/5189699/… Commented Aug 31, 2017 at 9:27
  • Hrabal and Domoarrigato: Thanks for both your answers. Those helped me to dig deeper about the concept of descriptor and instance attribute. I'll post the solution that worked for me. Commented Sep 21, 2017 at 16:53

1 Answer 1

0

I found this article and came up with solution to add the descriptors to an object.

This solution below worked for me:

  1. Add __getattribute__ and __setattr__ into Item Class

    # filename: item.py
    
    import abc
    
    
    class Item(Page):
        __metaclass__ = abc.ABCMeta
    
        def __init__(self, context):
            self.__context = context
            super(Item, self).__init__(
                self.__context.browser
            )
    
        def __getattribute__(self, name):
            value = object.__getattribute__(self, name)
            if hasattr(value, '__get__'):
                value = value.__get__(self, self.__class__)
            return value
    
        def __setattr__(self, name, value):
            try:
                obj = object.__getattribute__(self, name)
            except AttributeError:
                pass
            else:
                if hasattr(obj, '__set__'):
                    return obj.__set__(self, value)
            return object.__setattr__(self, name, value)
    
  2. Move class attribute to instance attribute in ItemList constructor

    # filename: item_list.py
    
    # locators dictionary is intentionally placed outside `ItemList` class
    locators = {
        'item_id': 'css=tr:nth-of-type({item_num}) .id',
        'item_title': 'css=tr:nth-of-type({item_num}) .alert-rules',
        # other properties
    }
    
    
    class ItemList(Item):
        def __init__(self, context, item_num=0):
            self.item_num = item_num
            # self.item_num is able to be passed to locators
            self.item_id = TextReadOnly(locators['item_id'].format(item_num=self.item_num))
            self.item_title = TextReadOnly(locators['item_title'].format(item_num=self.item_num))
    
            super(ItemList, self).__init__(
                context
            )
    
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.