injecting variables into functions; python
Question: I have a webdriver class that assumes there is only one driver. This is bad
because it can't handle multiple pages at once. I want to make a decorator
that will inject `self.driver` into any function decorated if it exists, if
not it will allow any function to use the driver passed to it. I should be
able to define and function as

    
    
    @get_driver
    def this_func(**kwargs):
        #I have access to 'driver' if I have self.driver or if a driver kwarg was given
    

Here it is:

    
    
    import os, time, subprocess, random
    
    from functools import wraps
    
    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.support.select import Select
    
    from pyvirtualdisplay import Display
    
    class get_driver(object):
    
        def __init__(self, func):
            self.func = func
            wraps(func)(self)
    
        def __call__(self, *args, **kwargs):
    
            try:
                kwargs.update({'driver': self.driver})
            except:
                pass
            return_ = self.func(*args, **kwargs)
            return return_
    
    class WebdriverChauffuer(object):
    
        def __init__(self, username=None, password=None, start_url=None):
            self.username = username
            self.password = password
            self.start_url = start_url
    
        @get_driver
        def source_code(self, **kwargs):
            return driver.page_source or None
    
    class FirefoxDriver(WebdriverChauffuer):
    
        def __init__(self, username=None, password=None, start_url=None, driver=None):
            super(FirefoxDriver, self).__init__(username=username, password=password, start_url=start_url)
    
        def start_driver(self):
            self.driver = webdriver.Firefox()
    

I am getting a strange error that no args were given, even though I call
`source_code` on an instance, which should give it self:

    
    
    In [1]: from my_scripting_library import *
    
    In [2]: d = FirefoxDriver()
    
    In [3]: d.start
    d.start_driver  d.start_url     
    
    In [3]: d.start_driver()
    
    In [4]: d.get('https://google.com')
    
    In [5]: d.source_code()
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-5-7419cf5df019> in <module>()
    ----> 1 d.source_code()
    
    /home/cchilders/scripts/my_scripting_library/webdriver/general.pyc in __call__(self, *args, **kwargs)
         24         except:
         25             pass
    ---> 26         return_ = self.func(*args, **kwargs)
         27         return return_
         28 
    
    TypeError: source_code() takes exactly 1 argument (0 given)
    

There's no difference when I change the return to

    
    
    return_ = self.func(**kwargs)
    

Why can't I call `source_code` anymore? Thank you

EDIT:

These drivers are to be used for purposes of course, here's an example:

    
    
    class HCCDriver(FirefoxDriver):
    
        def __init__(self, init=False):
            super(HCCDriver, self).__init__(start_url="https://hccadvisor.hccfl.edu")
    
        def main_page(self):
            self.get('https://www.hccfl.edu/hawknet.aspx')
    
        def login_webadvisor(self, username="cchilders", password="miley_cirus_is_great_singer", driver=None):
            self.webadvisor_driver = FirefoxDriver()
            webadvisor_driver.get(self.start_url)
            time.sleep(2)
            driver.access_link(search_text="Log In")
            driver.find_box_and_fill(search_text="LABELID_USER_NAME", value=username)
            driver.find_box_and_fill(search_text="CURR.PWD", value=password)
            driver.submit_form(search_text="SUBMIT")
            driver.access_link(search_text="Students")
    
        def login_email(self):
            self.start_driver()
            self.get("http://outlook.com/hawkmail.hccfl.edu")
            # WebDriverWait(self.driver, 10).until(EC.presence_of_element_located(By.ID, 'ctl00_ContentPlaceHolder1_UsernameTextBox'))
            self.find_box_and_fill(search_text="ctl00_ContentPlaceHolder1_UsernameTextBox", value="cchilders@hawkmail.hccfl.edu")
            self.find_box_and_fill(search_text="ctl00_ContentPlaceHolder1_PasswordTextBox", value="i_love_honey_booboo")
            time.sleep(2)
            self.submit_form("ctl00_ContentPlaceHolder1_SubmitButton")
    
        def login_myhcc(self):
            driver = FirefoxDriver()
            driver.get("https://hcc.instructure.com")
            time.sleep(5)
            find_box_and_fill('ctl00_ContentPlaceHolder1_UsernameTextBox', 'cchilders@hawkmail.hccfl.edu')
            driver.find_box_and_fill('ctl00_ContentPlawebadvisor_urlceHolder1_PasswordTextBox', 'if_evolution_was_real_americans_would_stop_worshipping_pres_candidates')
            driver.click_button('ctl00$ContentPlaceHolder1$SubmitButton')
    

The point is, without a decorator, I foresaw every single function looking
like:

    
    
    def this_func(self, driver=None):
        if not driver:
            try:
                driver = self.driver
            except:
                raise Exception('There is no driver my good sir')
    

with the `driver=None` and

    
    
        try:
            driver = self.driver
        except:
            raise Exception('There is no driver good sir')
    

parts being repetitive 20, 30 times, etc


Answer: Because your decorator is implemented as a class rather than a function, your
decorated `source_code` function is not being wrapped in a bound `method`
descriptor, since this is done to functions, not classes.

That means when you call `self.func`, no `self` argument is being passed in.
But your `source_code` method expects a `self` argument, which results in the
error `source_code() takes exactly 1 argument (0 given)`

On more recent versions of Python 3 you should be getting `foo() missing 1
required positional argument: 'self'`. I'm not sure when they added that
clarified message.

The comments have given you some ideas about different approaches, but that
explains your error.

