Selenium core for Python
Project description
Selenium CORE for Python
Introduction
It's a library with core functions simplifying work with Selenium-controlled applications.
This package is based on Aquality Selenium CORE for .NET and provides a set of methods related to the most common actions performed with elements. So you shouldn't have a lot of difficulties with this solution if you interacted with Aquality Selenium CORE.
To simplify overriding of implementations this solution uses Dependency Injection.
Supported Python Versions
- Python 3.7-3.12
Installation
If you have pip on your system, you can simply install or upgrade the Python bindings:
pip install py-selenium-auto-core
Alternately, you can download the source distribution from PyPI, unarchive it, and run:
python setup.py install
Quick start
- Setup Dependency Injection container using Startup
The solution offers a ServiceProvider implementation using Dependency Injection container as a single point for interacting with various services and their dependencies:
class ServiceProvider(containers.DeclarativeContainer):
"""Container that allows to resolve dependencies for all services in the library"""
settings_file: Singleton[JsonSettingsFile] = Singleton(JsonSettingsFile({}))
application: Factory[Application] = Factory(Application)
logger: Singleton[Logger] = Singleton(Logger)
logger_configuration: Singleton[LoggerConfiguration] = Singleton(LoggerConfiguration, settings_file)
timeout_configuration: Singleton[TimeoutConfiguration] = Singleton(TimeoutConfiguration, settings_file)
localization_manager: Singleton[LocalizationManager] = Singleton(LocalizationManager, logger_configuration, logger)
...
This allows you to control dependencies between packages and cause the creation of the objects themselves at the time of accessing them. Example of working with ServiceProvider:
ServiceProvider.logger().info("Message") # message logging
ServiceProvider.timeout_configuration().interval # Getting the Timeout.interval value
For ease of use, the solution contains a Startup object that makes it easy to get a ServiceProvider object with redefined dependencies for settings_file and application:
class Startup:
@staticmethod
def configure_services(application_provider: Callable, settings: Optional[JsonSettingsFile] = None, service_provider: Optional[T] = None,
) -> T | ServiceProvider:
service_provider: T = service_provider or ServiceProvider()
settings = settings or Startup.get_settings()
service_provider.settings_file.override(Singleton(lambda: settings))
service_provider.application.override(Factory(application_provider))
return service_provider
@staticmethod
def get_settings() -> JsonSettingsFile:
profile_name = EnvironmentConfiguration.get_variable("profile")
settings_profile = "settings.json" if not profile_name else f"settings.{profile_name}.json"
if FileReader.is_resource_file_exist(settings_profile, root_path=RootPathHelper.calling_root_path()):
return JsonSettingsFile(setting_name=settings_profile, root_path=RootPathHelper.calling_root_path())
return JsonSettingsFile(setting_name=settings_profile, root_path=RootPathHelper.executing_root_path())
service_provider = Startup.configure_services(application_provider=lambda: YourApplication())
service_provider.application()
service_provider.logger().info("Message")
To use a different implementation between dependencies, you need to execute provider.override:
service_provider = ServiceProvider()
service_provider.timeout_configuration.override(Singleton(CustomTimeoutConfiguration, service_provider.settings_file))
If you want to have your own CustomServiceProvider implementation that complements the base container, you can use inheritance, as in the example below:
class CustomServiceProvider(ServiceProvider):
timeout: Sigleton[CustomTimeoutConfiguration] = Singleton(CustomTimeoutConfiguration, ServiceProvider.settings_file)
Keep in mind that the internal dependencies of the ServiceProvider container will interact with ServiceProvider.timeout, and not with CustomServiceProvider.timeout. The solution is quite simple:
ServiceProvider.override(CustomServiceProvider)
service_provider = Startup.configure_services(application_provider=lambda: YourApplication(), service_provider=CustomServiceProvider())
ServiceProvider.reset_override() # necessary because override overrides the base container
or add your implementation of Startup:
class CustomSPStartup(Startup):
@staticmethod
def configure_services(
application_provider: Callable, settings: Optional[JsonSettingsFile] = None, service_provider: Optional[ServiceProvider] = None,
) -> CustomServiceProvider:
ServiceProvider.override(CustomServiceProvider)
settings = JsonSettingsFile("settings.special.json", RootPathHelper.calling_root_path())
service_provider = Startup.configure_services(application_provider, settings, CustomServiceProvider())
ServiceProvider.reset_override()
return service_provider
- Setup BrowserService using CoreServices
The solution also contains a CoreService, which helps to register your container and application, with which you can interact in the future:
The simplest way is to create your own Services class extended from abstract CoreServices with the following simple signature:
class BrowserServices(CoreServices):
@classmethod
def is_application_started(cls) -> bool:
return cls._is_application_started()
@classmethod
def application(cls) -> YourApplication:
return cls._get_application(lambda service: cls._start_application(service))
@classmethod
def service_provider(cls) -> ServiceProvider:
return cls._get_service_provider(lambda service: cls.application())
@classmethod
def _start_application(cls, service_provider: ServiceProvider):
... # your implementation
If you need to register your own services / rewrite the implementation, you need override Startup and implement BrowserServices like in example below:
class TestStartup(Startup):
@staticmethod
def configure_services(
application_provider: Callable,
settings: Optional[JsonSettingsFile] = None,
service_provider: Optional[ServiceProvider] = None,
) -> ServiceProvider:
settings = JsonSettingsFile("settings.special.json", RootPathHelper.calling_root_path())
service_provider = Startup.configure_services(application_provider, settings)
service_provider.timeout_configuration.override(
Singleton(TestTimeoutConfiguration, service_provider.settings_file)
)
return service_provider
class BrowserServices:
class _BrowserService(CoreServices):
startup: TestStartup = TestStartup()
def __init__(self):
Logger.info("Create")
@property
def application(self) -> Application:
return self._get_application(
self._start_function,
lambda: self.startup.configure_services(lambda service: self.application),
)
@property
def service_provider(self) -> ServiceProvider:
return self._get_service_provider(
lambda service: self.application,
lambda: self.startup.configure_services(lambda service: self.application),
)
def set_startup(self, startup: Startup):
if startup is not None:
self.startup = startup
@property
def _start_function(self):
return lambda serive: Application()
Instance: _BrowserService = _BrowserService()
class CustomStartup(Startup):
@staticmethod
def configure_services(application_provider: Callable, settings: JsonSettingsFile = None) -> ServiceProvider:
service_provider = Startup.configure_services(application_provider, settings)
# your implementation service_provider.timeout_configuration.override(Singleton(TimeoutConfiguration, service_provider.settings_file))
return service_provider
- Work with Application via the implemented BrowserServices or via element services
All the services could be resolved from the Dependency Injection container via ServiceProvider
BrowserServices.application().driver.find_element(ELEMENT).click()
BrowserServices.service_provider().conditional_wait().wait_for_driver(
lambda driver: len(driver.find_elements(Locator(By.XPATH, "//*"))) > 0
)
or with Instance:
BrowserServices.Instance.application.driver.find_element(ELEMENT).click()
BrowserServices.Instance.service_provider.conditional_wait().wait_for_driver(
lambda driver: len(driver.find_elements(Locator(By.XPATH, "//*"))) > 0
)
License
Library's source code is made available under the Apache 2.0 license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for py_selenium_auto_core-0.5.5.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 07f4a3b53b0e5a704f118b519d4293ddf66e233c1e7cac10dca4cde33194b2f4 |
|
MD5 | 74d0351fa2fc8e07805b4e356023e9b1 |
|
BLAKE2b-256 | 9bd93fd51e1920461efcbcd2e43461e0f596b18934e762166c32c5fe72b44066 |
Hashes for py_selenium_auto_core-0.5.5-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1a595bb9567f2b06359ee805196b7b87f002c8435f40285becba244350c0d4d8 |
|
MD5 | 3657bb5dbff8c79d7e43de74d1fdfdab |
|
BLAKE2b-256 | 6b2bc97bc9fa74f2a5a093e272d75d6ef6f6de54194178d7b0ccf72e77358c0f |