Ejemplo n.º 1
0
 def accept(self,
            key: str = None,
            timeout: int = 3600,
            **opt) -> Union[tpsup.nettools.encryptedsocket, None]:
     verbose = opt.get('verbose', 0)
     if verbose:
         tplog(
             f"waiting for new client connection. time out after {timeout} idle seconds"
         )
     selector = _ServerSelector()
     selector.register(self.socket, selectors.EVENT_READ)
     poll_interval = 1
     waited_so_far = 0
     while waited_so_far < timeout:
         if verbose > 2:
             print("looping")
         ready = selector.select(poll_interval)
         if ready:
             (clientsocket, address) = self.socket.accept()
             if verbose:
                 tplog(
                     f"accepted client socket {clientsocket}, address={address}"
                 )
             return tpsup.nettools.encryptedsocket(
                 established_socket=clientsocket, key=key)
         else:
             waited_so_far += poll_interval
     return None
Ejemplo n.º 2
0
def timeout_child(conn: multiprocessing.connection.Connection,
                  func: types.FunctionType, *args, **kwargs):
    """
    wrapper function for timeout_func
    :param conn:
    :param func:
    :param args:
    :param kwargs:
    :return:
    """
    verbose = kwargs.get('verbose', 0)
    if verbose:
        tplog(f"func={func}")
    if not func:
        # https://stackoverflow.com/questions/43369648/cant-get-attribute-function-inner-on-module-mp-main-from-e-python
        # when the multiprocessing library copies your main module, it won't run it as the __main__ script and
        # therefore anything defined inside the if __name__ == '__main__' is not defined in the child process
        # namespace. Hence, the AttributeError
        message = f"both func={func} is not initialized. note: func cannot be defined in __main__"
        tb = pformat(
            traceback.format_stack())  # outside exception use format_stack()
        conn.send(RuntimeError(f"{message}\n{tb}"))
    else:
        result = None
        try:
            result = func(*args, **kwargs)
            conn.send(result)
        except Exception as e:
            tb = pformat(
                traceback.format_exc())  # within exception use format_exc()
            conn.send(
                RuntimeError(
                    f"child process exception, pid={os.getpid()}\n{tb}"))
    conn.close()
Ejemplo n.º 3
0
    def __init__(self,
                 key: str,
                 established_socket: socket.socket = None,
                 host_port: str = None,
                 maxtry: int = 5,
                 try_interval: int = 3,
                 **opt):
        if established_socket:
            self.socket = established_socket
        elif host_port:
            host, port = host_port.split(':')
            if not port:
                raise RuntimeError(
                    f"bad format at host_port='{host_port}'; expected host:port"
                )
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            if sock:
                for i in range(0, maxtry):
                    try:
                        sock.connect((host, int(port)))
                        # tplog(f"connected to {sock}")
                        # __init__ connected to <socket.socket fd=3, family=AddressFamily.AF_INET,
                        # type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 36690), raddr=('127.0.0.1', 29999)>
                        laddr = sock.getsockname()
                        raddr = sock.getpeername()
                        tplog(
                            f"connected: local {laddr[0]}:{laddr[1]}, remote {raddr[0]}:{raddr[1]}"
                        )
                        tplog(
                            f"connected: local {laddr[0]}:{laddr[1]}, remote {raddr[0]}:{raddr[1]}"
                        )
                        self.socket = sock
                        break
                    except Exception as e:
                        tplog(
                            f'{i + 1} try out of {maxtry} failed to connect: {e}',
                            file=sys.stderr)
                        if i + 1 < maxtry:
                            tplog(f'will retry after {try_interval}',
                                  file=sys.stderr)
                            time.sleep(try_interval)
                        else:
                            raise RuntimeError(
                                f"failed to connect to {host}:{port} in {maxtry} tries"
                            )
            else:
                raise RuntimeError(socket.error)
        else:
            raise RuntimeError(
                "neither established_socket nor host_port specified")

        if key is None or key == '':
            key = None
        self.key = key

        self.in_coder = tpsup.coder.Coder(key)
        self.out_coder = tpsup.coder.Coder(key)
Ejemplo n.º 4
0
 def get_attrs(self,
               element: WebElement,
               method: str = 'bs4',
               verbose: int = 0):
     """
     get list of attributes from an element.
     https://stackoverflow.com/questions/27307131/selenium-webdriver-how-do-i-find-all-of-an-elements-attributes
     somehow, webdriver doesn't have an API for this
     :param element:
     :param method:
     :return:
     """
     """
     note: for html '<div class="login-greeting">Hi LCA Editor Tester,</div>'
     bs4 will give: {'class': ['login-greeting']}
     js  will give: {'class':  'login-greeting' }  
     """
     if method == 'bs4':
         html: str = element.get_attribute('outerHTML')
         if verbose:
             tplog(f"outerHTML={html}")
         if html:
             attrs = {}
             soup = BeautifulSoup(html, 'html.parser')
             # https://www.crummy.com/software/BeautifulSoup/bs4/doc/#attributes
             for element in soup():
                 # soup() is a generator
                 # element.attrs is a dict
                 attrs.update(element.attrs)
             return attrs
         else:
             return {}
     elif method == 'js':
         # java script. duplicate attributes will be overwritten
         js_script = 'var items = {}; ' \
                     'for (index = 0; index < arguments[0].attributes.length; ++index) { ' \
                     '   items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value' \
                     '};' \
                     'return items;'
         attrs = self.driver.execute_script(js_script, element)
         return attrs
     else:
         raise RuntimeError(
             f"unsupported method={method}. accepted: bs4 or js")
Ejemplo n.º 5
0
def main():
    print(
        '------- test timeout_func_unix(). should work on Unix and fail on windows'
    )
    try:
        sleep_5 = timeout_func_on_unix(2, module_sleep_and_tick, 3)
        sleep_5(10)
    except Exception as e:
        # error on Windows
        # AttributeError: module 'signal' has no attribute 'SIGALRM'
        print_exception(e)

    def main_sleep_and_tick(duration: int, *args, **opt) -> str:
        """
        this is local function:
        https://stackoverflow.com/questions/36994839/i-can-pickle-local-objects-if-i-use-a-derived-class

        """
        print(f"args = {pformat(args)}")
        print(f"opt = {pformat(opt)}")

        for i in range(duration):
            time.sleep(1)
            print('tick')
        message = f"pid={os.getpid()}"
        print(message)
        return message

    b = main_sleep_and_tick
    print(f"type={type(module_sleep_and_tick)}")
    print(
        '------- test timeout_func(). with function defined in module, should work but will time out'
    )
    try:
        result = timeout_func(2,
                              module_sleep_and_tick,
                              10,
                              message="should timeout")
        tplog(f"result={pformat(result)}")
    except TimeoutException as e:
        tplog_exception(e)
        tplog("got expected exception\n\n")

    print(
        '------- test timeout_func(). with function defined in main, may work on unix but should fail on windows '
        'with pickle error')
    try:
        result = timeout_func(2,
                              main_sleep_and_tick,
                              10,
                              message="should timeout")
        tplog(f"result={pformat(result)}")
    except AttributeError as e:
        # AttributeError: Can't pickle local object 'main.<locals>.main_sleep_and_tick'
        tplog_exception(e)
        tplog("got expected exception\n\n")

    print('\n------- test timeout_func(). child will raise exception')
    try:
        result = timeout_func(3, test_child_exception)
        tplog(f"result={pformat(result)}")
    except TimeoutException as e:
        tplog_exception(e)
        tplog("got expected exception\n\n")
Ejemplo n.º 6
0
def timeout_func(timeout: int, func: Callable, *args, **kwargs):
    """
    time out a func. signal.ALRM not working on Windows. therefore this
    https://stackoverflow.com/questions/492519/timeout-on-a-function-call

    so we use multiprocessing.Pipe() to do this
    https://docs.python.org/3.8/library/multiprocessing.html
    use this method because we can
    1. multiprocessing is process-based. process is easier to check and kill after timeout
    2. p.join(timeout) is easier to set timeout
    3. Pipe() is easier to collect return value

    there is decorator using a same method. It has covered a lot of edge conidtions. good place to learn
    https://github.com/bitranox/wrapt_timeout_decorator/blob/master/wrapt_timeout_decorator/wrap_function_multiprocess.py
    """

    # multiprocessing cannot run child with function defined in __main__
    #   https://stackoverflow.com/questions/43369648/cant-get-attribute-function-inner-on-module-mp-main-from-e-python
    #   when the multiprocessing library copies your main module, it won't run it as the __main__ script
    #   and therefore anything defined inside the if __name__ == '__main__' is not defined in the child
    #   process namespace. Hence, the AttributeError
    # I tried to reload module or delay import module to work around this. neither worked.
    # re-import failed to work around
    #   importlib.reload(multiprocessing)
    #   importlib.reload(multiprocessing.connection)
    # delayed import failed to work around
    #   import multiprocessing
    #   import multiprocessing.connection

    # the parent process can always see the process defined in __main__
    #   tplog(getattr(sys.modules['__mp_main__'], 'local_sleep_and_tick'))

    parent_conn, child_conn = multiprocessing.Pipe(duplex=False)

    # how to pass args and kwargs
    #   https://stackoverflow.com/questions/38908663/python-multiprocessing-how-to-pass-kwargs-to-function
    # p = multiprocessing.Process(target=timeout_child, args=(child_conn, func, *args), kwargs=kwargs)
    p = multiprocessing.Process(target=timeout_child,
                                args=(child_conn, func, *args),
                                kwargs=kwargs)

    # kill child after parent exits
    # https://stackoverflow.com/questions/25542110/kill-child-process-if-parent-is-killed-in-python
    p.daemon = True

    p.start()

    # Wait for timeout seconds or until process finishes
    p.join(timeout)

    # If thread is still active
    if p.is_alive():
        message = f"timed out after {timeout} second(s), terminating pid={p.pid}"
        tplog(message)
        traceback.print_stack(
        )  # default to stderr, set file=sys.stdout for stdout

        # Terminate
        p.terminate()
        p.join(0.5)
        parent_conn.close()
        raise TimeoutException(timeout=timeout, child_pid=p.pid)
    else:
        result = None
        if parent_conn.poll(0.1):
            # recv() is a blocked call, therefore, poll() first
            result = parent_conn.recv()
            parent_conn.close()
            if isinstance(result, Exception):
                raise result
            else:
                # child finished and returned anything
                return result
        else:
            # child finished but didn't return anything
            parent_conn.close()
Ejemplo n.º 7
0
    def send_and_encode(self,
                        data: Union[bytes, str],
                        data_is_file: bool = False,
                        timeout: int = 6,
                        **opt) -> int:
        """ this is actually use unblocked send() to re-implement a blocked sendall().
        The possible benefits:
        - add encryption
        """

        file = None
        fd = None
        if data_is_file:
            file = data
            size = os.path.getsize(file)
            fd = os.open(file, os.O_RDWR)  # fd is int
            data = mmap.mmap(fd, size, access=mmap.ACCESS_READ)
            # tplog(f"data type = {type(data)}")  #

        # https://docs.python.org/3/library/socket.html
        #         socket.send() vs socket.sendall()
        #
        #         socket.send(bytes[, flags])
        #         Send data to the socket. Returns the number of bytes sent. Applications are responsible
        #         for checking that all data has been sent; if only some of the data was transmitted, the application
        #         needs to attempt delivery of the remaining data.
        #
        #         socket.sendall(bytes[, flags])
        #         Send data to the socket. Unlike send(), this method continues to
        #         send data from bytes until either all data has been sent or an error occurs. None is returned on success. On
        #         error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent.

        saved_timeout = self.socket.gettimeout()
        # saved_blocking = self.socket.getblocking() this only available when version >= 3.7

        # unblock
        self.socket.setblocking(
            0
        )  # None: blocking mode; 0: non-blocking mode; positive floating: timeout mode.
        # this is the same as self.socket.settimeout(0)

        polling_interval = 1
        wait_so_far = 0
        total_sent = 0
        data_length = len(data)

        while total_sent < data_length:
            buffer_size = 4096
            new_end = total_sent + buffer_size
            if new_end > data_length:
                new_end = data_length
                buffer_size = new_end - total_sent

            buffer_bytes = self.out_coder.xor(data[total_sent:new_end])

            buffer_sent = 0
            while buffer_sent < buffer_size:
                ready = select.select([], [self.socket], [], polling_interval)
                if ready[1]:
                    sent = self.socket.send(buffer_bytes[buffer_sent:])
                    # there may be exceptions here
                    if sent == 0:
                        tplog(f"Remote connection is closed during our send()")
                        break
                    buffer_sent += sent
                    total_sent += sent
                else:
                    wait_so_far += polling_interval
                    if wait_so_far >= timeout:
                        tplog(f"send() timed out after {wait_so_far} seconds")
                        break
            else:
                # the above loop did NOT break = the above loop condition was False = the loop ended naturally
                # python's way to break nested loop (double loop)
                # https://stackoverflow.com/questions/653509/breaking-out-of-nested-loops
                continue
            break  # the above loop DID break = the above loop condition was still True = the loop didn't end naturally

        if fd:
            os.close(fd)

        missing = data_length - total_sent
        if missing > 0:
            tplog(
                f"Sent total {total_sent} bytes. failed to send {missing} bytes"
            )
        else:
            tplog(f"Sent all {total_sent} bytes")

        # getblocking()/setblocking() is implemented by gettimeout()/settimeout. no getblocking() before 3.7
        self.socket.settimeout(saved_timeout)
        # self.socket.setblocking(saved_blocking) # restoring blocking setting

        return total_sent
Ejemplo n.º 8
0
    def recv_and_decode(self,
                        timeout: int = 6,
                        maxsize=1024 * 1024 * 1024,
                        file: str = None,
                        **opt) -> Union[bytes, int]:
        """ this is actually use unblocked recv() to re-implement a blocked recv().
        The possible benefits:
        - sock.recv(buffer_size)'s buffer_size is limited, we can use a loop to recv() bigger file. In this
        loop pattern, socket.settimeout() looks awkward. socket.settimeout() seems better for
        small data
        - decrypt the incoming data with smaller chunks, ie, in buffer_size
        """

        if file:
            # receive data will write to this file
            fh = open(file, 'wb')
        else:
            fh = None

        # https://docs.python.org/3/library/socket.html

        # getblocking()/setblocking() is implemented by gettimeout()/settimeout. no getblocking() before 3.7
        saved_timeout = self.socket.gettimeout()
        # saved_blocking = self.socket.getblocking() this only available when version >= 3.7

        # unblock
        self.socket.setblocking(
            0
        )  # None: blocking mode; 0: non-blocking mode; positive floating: timeout mode.

        polling_interval = 1
        wait_so_far = 0
        total_size = 0
        received_bytearray = bytearray()
        while wait_so_far < timeout:
            ready = select.select(
                [self.socket], [], [],
                polling_interval)  # last arg is timeout in seconds
            if ready[0]:
                data = self.socket.recv(4096)
                # there may be exceptions here
                if data == b'':
                    tplog(f"Remote connection is closed during our recv()")
                    break

                if fh:
                    fh.write(self.in_coder.xor(data))
                else:
                    received_bytearray.extend(self.in_coder.xor(data))

                total_size += len(data)
                if total_size > maxsize:
                    tplog(
                        f"Received {total_size} bytes already exceeded maxsize {maxsize}. Stopped receiving."
                    )
                    break
            else:
                # time.sleep(polling_interval)  # no need sleep here if we already blocked at select()
                wait_so_far += polling_interval
        if fh:
            fh.close()
        if wait_so_far >= timeout:
            tplog(f"recv() timed out after {wait_so_far} seconds")
        tplog(f"Received total {total_size} bytes")

        # getblocking()/setblocking() is implemented by gettimeout()/settimeout. no getblocking() before 3.7
        self.socket.settimeout(saved_timeout)
        # self.socket.setblocking(saved_blocking) # restoring blocking setting

        if file:
            return total_size
        else:
            return bytes(received_bytearray)
Ejemplo n.º 9
0
def run(seleniumEnv: tpsup.seleniumtools.SeleniumEnv, **opt):
    verbose = opt.get('verbose', 0)
    mod_file = opt.get('mod_file', 'mod_file')

    argList = opt.get('argList', [])

    if verbose:
        sys.stderr.write(f"{mod_file} argList=\n")
        sys.stderr.write(pformat(argList) + "\n")

    parser = argparse.ArgumentParser(prog=mod_file, )
    parser.add_argument('-js',
                        dest='use_javascript',
                        default=False,
                        action='store_true',
                        help='use javascript')

    parser.add_argument('-wait',
                        dest='wait',
                        default=5,
                        action='store',
                        type=int,
                        help='rename the file. default not to rename')

    parser.add_argument('-rename',
                        dest='renamed',
                        default=None,
                        action='store',
                        help='rename the file. default not to rename')

    args = vars(parser.parse_args(argList))

    if not verbose:
        verbose = args.get('verbose', 0)

    if verbose:
        tplog(f"args = {pformat(args)}")

    driver = seleniumEnv.get_driver()

    url = f"http://livingstonchinese.org/LCA2/index.php/join-us"
    driver.get(url)

    # https://stackoverflow.com/questions/46937319/how-to-use-chrome-webdriver-in-selenium-to-download-files-in-python
    driver.command_executor._commands["send_command"] = (
        "POST", '/session/$sessionId/chromium/send_command')
    params = {
        'cmd': 'Page.setDownloadBehavior',
        'params': {
            'behavior': 'allow',
            'downloadPath': seleniumEnv.download_dir
        }
    }
    command_result = driver.execute("send_command", params)

    # https://dev.to/endtest/a-practical-guide-for-finding-elements-with-selenium-4djf

    # <a style="color: #1b57b1; font-weight: normal; text-decoration: none;"
    # href="/LCA2/images/docs/public/lca_bylaw_2019_11.pdf">lick to view LCA By Law</a>
    #elem = driver.find_element_by_css_selector("#content > div.item-page > div:nth-child(4) > pre:nth-child(15) > span > a")
    elem = driver.find_element_by_partial_link_text("view LCA By Law")

    src = elem.get_attribute("href")
    env = seleniumEnv.env
    download_dir = seleniumEnv.download_dir

    shortname = "lca.pdf"
    urllib.request.urlretrieve(src, f"{download_dir}/{shortname}")

    return download_dir
Ejemplo n.º 10
0
        sys.exit(1)

    tmpdir = tpsup.tptmp.tptmp().get_nowdir()

    # this is client mode if serverHostPort is defined
    ensock = tpsup.nettools.encryptedsocket(key, host_port=serverHostPort)

    request = {
        'mod_file': args['mod_file'],
        'args': args['remainingArgs'],
        'accept': args['accept'],
    }

    request_str = json.dumps(request)
    request_bytes = bytes(request_str, "utf-8")
    tplog(f"Sending {len(request_bytes)} bytes", file=sys.stderr)
    ensock.send_and_encode(request_bytes)

    # shut down the send channel so that the other side recv() won't wait forever
    ensock.send_shutdown()

    tplog("Sent. waiting response")
    if request['accept'] == 'json':
        received_bytes = ensock.recv_and_decode(
            timeout=60)  # this needs a long wait
        tplog(f"received {received_bytes} bytes")
        received_str = str(received_bytes, 'utf-8')
        received_structure = json.loads(received_str)
        tplog(f"Received data structure: {pformat(received_structure)}")
    elif request['accept'] == 'tar':
        tar_name = os.path.join(tmpdir, "reply.tar")
Ejemplo n.º 11
0
def run(seleniumEnv: tpsup.seleniumtools.SeleniumEnv, **opt):
    username = "******"  # change this to the username associated with your account
    verbose = opt.get('verbose', 0)
    mod_file = opt.get('mod_file', 'mod_file')

    argList = opt.get('argList', [])
    if verbose:
        sys.stderr.write(f"{mod_file}argList=\n")
        sys.stderr.write(pformat(argList) + "\n")

    parser = argparse.ArgumentParser(prog=mod_file, )
    parser.add_argument('-u',
                        dest='username',
                        default=username,
                        action='store',
                        help='login user')
    args = vars(parser.parse_args(argList))

    if not verbose:
        verbose = args.get('verbose', 0)

    if verbose:
        tplog(f"args = {pformat(args)}")

    username = args['username']

    entryBook = EntryBook()
    password = entryBook.get_entry_by_key(username).get('decoded')

    driver = seleniumEnv.get_driver()

    # print(f'driver.title={driver.title}')

    url = 'https://livingstonchinese.org/'
    driver.get(url)

    expected_url = "https://livingstonchinese.org/LCA2/"
    actual_url = driver.current_url
    assert expected_url == actual_url

    seleniumEnv.delay_for_viewer()  # give 1 sec to let the tail set up

    # https://dev.to/endtest/a-practical-guide-for-finding-elements-with-selenium-4djf
    # in chrome browser, find the interested spot, right click -> inspect, this will bring up source code,
    # in the source code window, right click -> copy -> ...

    # from Edge/Chrome, right click the item -> inspect
    login_elem = driver.find_element_by_id('modlgn-username')
    login_elem.send_keys(username)
    seleniumEnv.delay_for_viewer(1)  # delay to mimic humane slowness
    password_elem = driver.find_element_by_id('modlgn-passwd')
    password_elem.send_keys(password)

    # frameinfo = getframeinfo(currentframe())
    # print(frameinfo.filename, frameinfo.lineno, file=sys.stderr)  # print line number

    # from Edge/Chrome, right click the item -> inspect
    # because login button has no "id", so I used xpath. xpath is very sensitive to changes in the page
    driver.find_element_by_xpath(
        '/html/body/div[1]/div/div/div/div[1]/form/div/div[4]/div/button'
    ).click()
    seleniumEnv.delay_for_viewer(1)  # delay to mimic humane slowness

    # this doesn't work as 'button' is a grand-child of form-login-sutmit
    # driver.find_element_by_id('form-login-submit').click()

    # frameinfo = getframeinfo(currentframe())
    # print(frameinfo.filename, frameinfo.lineno, file=sys.stderr)

    xpath = '//*[@id=\"login-form\"]'
    elem = driver.find_element_by_xpath('//*[@id=\"login-form\"]')
    welcomeText = elem.text

    # frameinfo = getframeinfo(currentframe())
    # print(frameinfo.filename, frameinfo.lineno, file=sys.stderr)

    print(f"We see: {welcomeText}")
    # assert re.search("^Hi ", welcomeText)
    error = None
    pattern = "^Hi "
    if not re.search(pattern, welcomeText):
        error = f"xpath='{xpath}' failed to match pattern '{pattern}'"

    result = {'error': error, 'data': welcomeText}

    return result
Ejemplo n.º 12
0
def run(seleniumEnv: tpsup.seleniumtools.SeleniumEnv, **opt):
    verbose = opt.get('verbose', 0)
    mod_file = opt.get('mod_file', 'mod_file')

    argList = opt.get('argList', [])

    if verbose:
        sys.stderr.write(f"{mod_file} argList=\n")
        sys.stderr.write(pformat(argList) + "\n")

    parser = argparse.ArgumentParser(
        prog=mod_file,
    )
    parser.add_argument(
        '-js', dest='use_javascript', default=False, action='store_true',
        help='use javascript')

    parser.add_argument(
        '-wait', dest='wait', default=5, action='store', type=int,
        help='rename the file. default not to rename')

    parser.add_argument(
        '-rename', dest='renamed', default=None, action='store',
        help='rename the file. default not to rename')

    args = vars(parser.parse_args(argList))

    if not verbose:
        verbose = args.get('verbose',0)

    if verbose:
        tplog(f"args = {pformat(args)}")

    driver = seleniumEnv.get_driver()

    url = f"https://metacpan.org/pod/DBI::Log"
    tplog(f"going to url={url}")

    driver.get(url)

    # https://stackoverflow.com/questions/46937319/how-to-use-chrome-webdriver-in-selenium-to-download-files-in-python
    driver.command_executor._commands["send_command"] = ("POST", '/session/$sessionId/chromium/send_command')
    params = {'cmd': 'Page.setDownloadBehavior',
              'params': {'behavior': 'allow', 'downloadPath': seleniumEnv.download_dir}}
    command_result = driver.execute("send_command", params)

    time.sleep(1)

    # https://dev.to/endtest/a-practical-guide-for-finding-elements-with-selenium-4djf
    download_text = 'Download ('
    elem = driver.find_element_by_partial_link_text(download_text)

    elem.click()
    tplog(f"clicked '{download_text}'")

    wait_time_for_download = float(args['wait'])
    time.sleep(wait_time_for_download)

    # control file name
    # https://stackoverflow.com/questions/34548041/selenium-give-file-name-when-downloading
    files: List[str] = [os.path.join(seleniumEnv.download_dir, f) for f in os.listdir(seleniumEnv.download_dir)]
    if files:
        filename = max(files, key=os.path.getctime)
        renamed = args['renamed']
        if renamed:
            shutil.move(filename, os.path.join(seleniumEnv.download_dir, renamed))
            return renamed
        else:
            return filename
    else:
        tplog(f"no file downloaded to {seleniumEnv.download_dir}")
        raise RuntimeError(f"no file downloaded in {wait_time_for_download} seconds")
Ejemplo n.º 13
0
def run(seleniumEnv: tpsup.seleniumtools.SeleniumEnv, **opt):
    username = "******"  # change this to the username associated with your account
    verbose = opt.get('verbose', 0)
    mod_file = opt.get('mod_file', 'mod_file')
    argList = opt.get('argList', [])

    if verbose:
        sys.stderr.write(f"{mod_file}argList=\n")
        sys.stderr.write(pformat(argList) + "\n")

    parser = argparse.ArgumentParser(prog=mod_file, )
    parser.add_argument('-u',
                        dest='username',
                        default=username,
                        action='store',
                        help='login user')
    args = vars(parser.parse_args(argList))

    if not verbose:
        verbose = args.get('verbose', 0)

    if verbose:
        tplog(f"args = {pformat(args)}")

    username = args['username']

    entryBook = EntryBook()
    password = entryBook.get_entry_by_key(username).get('decoded')

    driver = seleniumEnv.get_driver()

    # print(f'driver.title={driver.title}')

    url = 'https://livingstonchinese.org/'
    driver.get(url)

    expected_url = "https://livingstonchinese.org/LCA2/"
    actual_url = driver.current_url
    assert expected_url == actual_url

    seleniumEnv.delay_for_viewer()  # give 1 sec to let the tail set up

    # https://dev.to/endtest/a-practical-guide-for-finding-elements-with-selenium-4djf
    # in chrome browser, find the interested spot, right click -> inspect, this will bring up source code,
    # in the source code window, right click -> copy -> ...
    found_logout = True
    need_to_login = False

    logout_elem = None
    logout_css_selector = '#login-form > div.logout-button > input.btn.btn-primary'

    try:
        logout_elem = driver.find_element_by_css_selector(logout_css_selector)
    except NoSuchElementException:
        found_logout = False

    if found_logout:
        tplog(
            f"found logout button, meaning someone already logged in. css_selector='{logout_css_selector}'"
        )
        need_to_logout = True
        greeting_text = None
        greeting_css = '#login-form > div.login-greeting'
        try:
            greeting_elem = driver.find_element_by_css_selector(greeting_css)
            if verbose:
                attrs = seleniumEnv.get_attrs(greeting_elem, method='bs4')
                tplog(f"attrs by bs4 = {pformat(attrs)}")
                attrs = seleniumEnv.get_attrs(greeting_elem, method='js')
                tplog(f"attrs by js = {pformat(attrs)}")
            greeting_text = greeting_elem.text
            tplog(f"greeting_text='{greeting_text}' at css='{greeting_css}'")
        except Exception as e:
            print_exception(e)
            tplog(
                f"cannot find greeting_text at css='{greeting_css}'. need to log out"
            )
        if greeting_text:
            greeting_pattern = "^Hi (.+),"
            m = re.search(greeting_pattern, greeting_text)
            if m:
                username = m.group(1)
                expected_username = "******"
                if not username == expected_username:
                    tplog(
                        f"username='******' did not match expected username='******'. need to log out"
                    )
                else:
                    need_to_logout = False
                    tplog(
                        f"username='******' matched expected username='******'. no need to log in again"
                    )
            else:
                tplog(
                    f"greeting_text='{greeting_text}', not matching expected greeting_pattern='{greeting_pattern}'. "
                    f"need to log out")
        else:
            tplog(
                f"cannot find greeting_text at css_selector={greeting_css}. need to log out"
            )

        if need_to_logout:
            need_to_login = True
            logout_elem.click()
            seleniumEnv.delay_for_viewer(1)
    else:
        tplog(f"seems nobody logged in. we will login.")
        need_to_login = True

    if need_to_login:
        # from Edge/Chrome, right click the item -> inspect
        login_elem = driver.find_element_by_id('modlgn-username')
        login_elem.send_keys(username)
        seleniumEnv.delay_for_viewer(1)  # delay to mimic humane slowness
        password_elem = driver.find_element_by_id('modlgn-passwd')
        password_elem.send_keys(password)

        # frameinfo = getframeinfo(currentframe())
        # print(frameinfo.filename, frameinfo.lineno, file=sys.stderr)  # print line number

        # from Edge/Chrome, right click the item -> inspect
        # because login button has no "id", so I used xpath. xpath is very sensitive to changes in the page
        driver.find_element_by_xpath(
            '/html/body/div[1]/div/div/div/div[1]/form/div/div[4]/div/button'
        ).click()
        seleniumEnv.delay_for_viewer(1)  # delay to mimic humane slowness

        # this doesn't work as 'button' is a grand-child of form-login-sutmit
        # driver.find_element_by_id('form-login-submit').click()

    # frameinfo = getframeinfo(currentframe())
    # print(frameinfo.filename, frameinfo.lineno, file=sys.stderr)

    xpath = '//*[@id=\"login-form\"]'
    elem = driver.find_element_by_xpath('//*[@id=\"login-form\"]')
    welcomeText = elem.text

    # frameinfo = getframeinfo(currentframe())
    # print(frameinfo.filename, frameinfo.lineno, file=sys.stderr)

    tplog(f"We see: {welcomeText}")
    # assert re.search("^Hi ", welcomeText)
    error = None
    pattern = "^Hi "
    if not re.search(pattern, welcomeText):
        error = f"xpath='{xpath}' failed to match pattern '{pattern}'"

    result = {'error': error, 'data': welcomeText}

    return result