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
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py_selenium_auto_core-0.5.6.tar.gz.
File metadata
- Download URL: py_selenium_auto_core-0.5.6.tar.gz
- Upload date:
- Size: 28.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9394df636aafac48ed66616bd859b2256b4f500e5bd423888f27d7bb237e191b
|
|
| MD5 |
fd5f0bae43ed248266ef936cd6eefdf5
|
|
| BLAKE2b-256 |
f928328dfe3b63595f03b590dd5b2dab394fe41c477e807f5fbc10bc65f5859b
|
File details
Details for the file py_selenium_auto_core-0.5.6-py3-none-any.whl.
File metadata
- Download URL: py_selenium_auto_core-0.5.6-py3-none-any.whl
- Upload date:
- Size: 39.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90583471ec5780583da0b610f261dbeed1fdb75234b4dc5b3421c29a20bb3060
|
|
| MD5 |
239182e7e182a0601440f999bf8c5c57
|
|
| BLAKE2b-256 |
bc2ad745f348ff1898df69568f0a357651501d1b2434ae146dcefd8365bae90a
|