diff --git a/.github/workflows/precommits.yml b/.github/workflows/precommits.yml index 6686968..2f2af30 100644 --- a/.github/workflows/precommits.yml +++ b/.github/workflows/precommits.yml @@ -43,10 +43,8 @@ jobs: run: | poetry run pytest tests/unit - - name: Lint check - run: | - poetry run pylint --disable=R,C playwright_stealth + - name: ruff check + uses: astral-sh/ruff-action@v3 - - name: Code style check - run: | - poetry run black playwright_stealth --check --diff + - name: ruff format + run: ruff format --check --diff diff --git a/.pylintrc b/.pylintrc index c84c81b..50fb040 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,640 +1,3 @@ [MAIN] - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Clear in-memory caches upon conclusion of linting. Useful if running pylint -# in a server-like mode. -clear-cache-post-run=no - -# Load and enable all available extensions. Use --list-extensions to see a list -# all available extensions. -#enable-all-extensions= - -# In error mode, messages with a category besides ERROR or FATAL are -# suppressed, and no reports are done by default. Error mode is compatible with -# disabling specific errors. -#errors-only= - -# Always return a 0 (non-error) status code, even if lint errors are found. -# This is primarily useful in continuous integration scripts. -#exit-zero= - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-allow-list= - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. (This is an alternative name to extension-pkg-allow-list -# for backward compatibility.) -extension-pkg-whitelist= - -# Return non-zero exit code if any of these messages/categories are detected, -# even if score is above --fail-under value. Syntax same as enable. Messages -# specified are enabled, while categories only check already-enabled messages. -fail-on= - -# Specify a score threshold under which the program will exit with error. -fail-under=10 - -# Interpret the stdin as a python script, whose filename needs to be passed as -# the module_or_package argument. -#from-stdin= - -# Files or directories to be skipped. They should be base names, not paths. -ignore=CVS - -# Add files or directories matching the regular expressions patterns to the -# ignore-list. The regex matches against paths and can be in Posix or Windows -# format. Because '\\' represents the directory delimiter on Windows systems, -# it can't be used as an escape character. -ignore-paths= - -# Files or directories matching the regular expression patterns are skipped. -# The regex matches against base names, not paths. The default value ignores -# Emacs file locks -ignore-patterns=^\.# - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -init-hook='import sys; sys.path.append(".")' - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use, and will cap the count on Windows to -# avoid hangs. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Minimum Python version to use for version dependent checks. Will default to -# the version used to run pylint. -py-version=3.11 - -# Discover python modules and packages in the file system subtree. -recursive=no - -# Add paths to the list of the source roots. Supports globbing patterns. The -# source root is an absolute path or a path relative to the current working -# directory used to determine a package namespace for modules located under the -# source root. -source-roots= - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - -# In verbose mode, extra non-checker-related info will be displayed. -#verbose= - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. If left empty, argument names will be checked with the set -# naming style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. If left empty, attribute names will be checked with the set naming -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Bad variable names regexes, separated by a comma. If names match any regex, -# they will always be refused -bad-names-rgxs= - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. If left empty, class attribute names will be checked -# with the set naming style. -#class-attribute-rgx= - -# Naming style matching correct class constant names. -class-const-naming-style=UPPER_CASE - -# Regular expression matching correct class constant names. Overrides class- -# const-naming-style. If left empty, class constant names will be checked with -# the set naming style. -#class-const-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. If left empty, class names will be checked with the set naming style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. If left empty, constant names will be checked with the set naming -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. If left empty, function names will be checked with the set -# naming style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - ex, - Run, - _ - -# Good variable names regexes, separated by a comma. If names match any regex, -# they will always be accepted -good-names-rgxs= - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. If left empty, inline iteration names will be checked -# with the set naming style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. If left empty, method names will be checked with the set naming style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. If left empty, module names will be checked with the set naming style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Regular expression matching correct type alias names. If left empty, type -# alias names will be checked with the set naming style. -#typealias-rgx= - -# Regular expression matching correct type variable names. If left empty, type -# variable names will be checked with the set naming style. -#typevar-rgx= - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. If left empty, variable names will be checked with the set -# naming style. -#variable-rgx= - - -[CLASSES] - -# Warn about protected attribute access inside special methods -check-protected-access-in-special-methods=no - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp, - asyncSetUp, - __post_init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# List of regular expressions of class ancestor names to ignore when counting -# public methods (see R0903) -exclude-too-few-public-methods= - -# List of qualified class names to ignore when counting class parents (see -# R0901) -ignored-parents= - -# Maximum number of arguments for function / method. -max-args=5 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Maximum number of boolean expressions in an if statement (see R0916). -max-bool-expr=5 - -# Maximum number of branch for function / method body. -max-branches=12 - -# Maximum number of locals for function / method body. -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body. -max-returns=6 - -# Maximum number of statements in function / method body. -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when caught. -overgeneral-exceptions=builtins.BaseException,builtins.Exception - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=1000 - -# Maximum number of lines in a module. -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow explicit reexports by alias from a package __init__. -allow-reexport-from-package=no - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules= - -# Output a graph (.gv or any supported image format) of external dependencies -# to the given file (report RP0402 must not be disabled). -ext-import-graph= - -# Output a graph (.gv or any supported image format) of all (i.e. internal and -# external) dependencies to the given file (report RP0402 must not be -# disabled). -import-graph= - -# Output a graph (.gv or any supported image format) of internal dependencies -# to the given file (report RP0402 must not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Couples of modules and preferred modules, separated by a comma. -preferred-modules= - - -[LOGGING] - -# The type of string formatting that logging methods do. `old` means using % -# formatting, `new` is for `{}` formatting. -logging-format-style=new - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, -# UNDEFINED. -confidence=HIGH, - CONTROL_FLOW, - INFERENCE, - INFERENCE_FAILURE, - UNDEFINED - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then re-enable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=raw-checker-failed, - bad-inline-option, - locally-disabled, - file-ignored, - suppressed-message, - useless-suppression, - deprecated-pragma, - use-symbolic-message-instead, - pointless-string-statement, - redefined-outer-name, - wrong-import-position, - logging-fstring-interpolation, - missing-class-docstring, - no-name-in-module, - missing-module-docstring, - fixme - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[METHOD_ARGS] - -# List of qualified names (i.e., library.method) which require a timeout -# parameter e.g. 'requests.api.get,requests.api.post' -timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - -# Regular expression of note tags to take in consideration. -notes-rgx= - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit,argparse.parse_error - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'fatal', 'error', 'warning', 'refactor', -# 'convention', and 'info' which contain the number of messages in each -# category, as well as 'statement' which is the total number of statements -# analyzed. This score is used by the global evaluation report (RP0004). -evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -#output-format= - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[SIMILARITIES] - -# Comments are removed from the similarity computation -ignore-comments=yes - -# Docstrings are removed from the similarity computation -ignore-docstrings=yes - -# Imports are removed from the similarity computation -ignore-imports=yes - -# Signatures are removed from the similarity computation -ignore-signatures=yes - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. No available dictionaries : You need to install -# both the python package and the system dependency for enchant to work.. -spelling-dict= - -# List of comma separated words that should be considered directives if they -# appear at the beginning of a comment and should not be checked. -spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=no - -# This flag controls whether the implicit-str-concat should generate a warning -# on implicit string concatenation in sequences defined over several lines. -check-str-concat-over-line-jumps=no - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of symbolic message names to ignore for Mixin members. -ignored-checks-for-mixins=no-member, - not-async-context-manager, - not-context-manager, - attribute-defined-outside-init - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# Regex pattern to define which classes are considered mixins. -mixin-class-rgx=.*[Mm]ixin - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of names allowed to shadow builtins -allowed-redefined-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - +# Pylint is disabled. Ruff is used instead +ignore-paths=* diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 572a3d0..37b9f37 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,3 @@ { - "recommendations": [ - "ms-python.black-formatter", - "ms-python.isort", - "ms-python.pylint", - ] + "recommendations": ["charliermarsh.ruff"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index d969f96..769a4f5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,16 @@ { + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + }, + "editor.formatOnSave": true, + "editor.rulers": [120], + "files.insertFinalNewline": true, + "python.analysis.typeCheckingMode": "standard", + "windsurfPyright.analysis.typeCheckingMode": "standard", "python.testing.pytestArgs": ["tests"], - "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true + "python.testing.pytestEnabled": true, + "python.testing.unittestEnabled": false } diff --git a/examples/stealth_mode.py b/examples/stealth_mode.py index 04b6d6c..9f2d446 100644 --- a/examples/stealth_mode.py +++ b/examples/stealth_mode.py @@ -3,9 +3,9 @@ import random from playwright.async_api import ProxySettings, async_playwright -from playwright_stealth import stealth_async -from playwright_stealth.core import StealthConfig, BrowserType +from playwright_stealth import stealth_async +from playwright_stealth.core import BrowserType, StealthConfig logging.basicConfig(level=logging.DEBUG) log = logging.getLogger(__name__) @@ -23,9 +23,12 @@ async def main(): proxy: ProxySettings | None = random.choice(PROXIES) if PROXIES else None - async with async_playwright() as playwright, await playwright.chromium.launch( - headless=False, - ) as browser: + async with ( + async_playwright() as playwright, + await playwright.chromium.launch( + headless=False, + ) as browser, + ): context = await browser.new_context(proxy=proxy) page = await context.new_page() await stealth_async(page, config=StealthConfig(browser_type=BrowserType.CHROME)) diff --git a/playwright_stealth/__init__.py b/playwright_stealth/__init__.py index 73113ec..789f46c 100644 --- a/playwright_stealth/__init__.py +++ b/playwright_stealth/__init__.py @@ -1,2 +1,3 @@ -# -*- coding: utf-8 -*- -from playwright_stealth.stealth import stealth_sync, stealth_async, StealthConfig +from playwright_stealth.stealth import StealthConfig, stealth_async, stealth_sync + +__all__ = ["StealthConfig", "stealth_async", "stealth_sync"] diff --git a/playwright_stealth/core/__init__.py b/playwright_stealth/core/__init__.py index 103ffa4..e75939b 100644 --- a/playwright_stealth/core/__init__.py +++ b/playwright_stealth/core/__init__.py @@ -1,3 +1,3 @@ -from ._stealth_config import StealthConfig, BrowserType +from ._stealth_config import BrowserType, StealthConfig -__ALL__ = ["StealthConfig", "BrowserType"] +__all__ = ["StealthConfig", "BrowserType"] diff --git a/playwright_stealth/core/_stealth_config.py b/playwright_stealth/core/_stealth_config.py index 072c11a..764552e 100644 --- a/playwright_stealth/core/_stealth_config.py +++ b/playwright_stealth/core/_stealth_config.py @@ -1,8 +1,9 @@ import json +import os from dataclasses import dataclass from typing import Optional -import os -from playwright_stealth.properties import Properties, BrowserType + +from playwright_stealth.properties import BrowserType, Properties def from_file(name) -> str: diff --git a/playwright_stealth/properties/__init__.py b/playwright_stealth/properties/__init__.py index 3ad6210..c5f73b4 100644 --- a/playwright_stealth/properties/__init__.py +++ b/playwright_stealth/properties/__init__.py @@ -1,3 +1,3 @@ -from ._properties import Properties, BrowserType +from ._properties import BrowserType, Properties -__ALL__ = ["Properties", "BrowserType"] +__all__ = ["Properties", "BrowserType"] diff --git a/playwright_stealth/properties/_properties.py b/playwright_stealth/properties/_properties.py index fec0f4d..b008764 100644 --- a/playwright_stealth/properties/_properties.py +++ b/playwright_stealth/properties/_properties.py @@ -1,12 +1,14 @@ -import re import random +import re from dataclasses import dataclass +from enum import Enum + from fake_http_header import FakeHttpHeader + from ._header_properties import HeaderProperties from ._navigator_properties import NavigatorProperties from ._viewport_properties import ViewportProperties from ._webgl_properties import WebGlProperties -from enum import Enum class BrowserType(Enum): @@ -17,7 +19,6 @@ class BrowserType(Enum): @dataclass class Properties: - header: HeaderProperties navigator: NavigatorProperties viewport: ViewportProperties @@ -39,9 +40,7 @@ def __init__(self, browser_type: BrowserType = BrowserType.CHROME): is not BrowserType.FIREFOX, # Firefox does not support client hints **spoofed_headers.as_header_dict(), ) - self.navigator = NavigatorProperties( - brands=brands, dnt=dnt, **spoofed_headers.as_header_dict() - ) + self.navigator = NavigatorProperties(brands=brands, dnt=dnt, **spoofed_headers.as_header_dict()) self.viewport = ViewportProperties() self.webgl = WebGlProperties() self.runOnInsecureOrigins = None @@ -82,9 +81,7 @@ def _generate_brands(self, user_agent: str, browser: str = "chrome") -> str: escaped_chars = [" ", " ", ";"] - greasey_brand = ( - f"{escaped_chars[order[0]]}Not{escaped_chars[order[1]]}A{escaped_chars[order[2]]}Brand" - ) + greasey_brand = f"{escaped_chars[order[0]]}Not{escaped_chars[order[1]]}A{escaped_chars[order[2]]}Brand" greased_brand_version_list = [{}, {}, {}] diff --git a/playwright_stealth/properties/_viewport_properties.py b/playwright_stealth/properties/_viewport_properties.py index 82490b6..4cff210 100644 --- a/playwright_stealth/properties/_viewport_properties.py +++ b/playwright_stealth/properties/_viewport_properties.py @@ -1,5 +1,5 @@ -from dataclasses import dataclass import random +from dataclasses import dataclass @dataclass diff --git a/playwright_stealth/stealth.py b/playwright_stealth/stealth.py index 690cb41..668d05e 100644 --- a/playwright_stealth/stealth.py +++ b/playwright_stealth/stealth.py @@ -1,8 +1,8 @@ -# -*- coding: utf-8 -*- from playwright.async_api import Page as AsyncPage from playwright.sync_api import Page as SyncPage + from playwright_stealth.core import StealthConfig -from playwright_stealth.properties import Properties, BrowserType +from playwright_stealth.properties import BrowserType, Properties def combine_scripts(properties: Properties, config: StealthConfig): diff --git a/poetry.lock b/poetry.lock index df3c384..e24f008 100644 --- a/poetry.lock +++ b/poetry.lock @@ -70,68 +70,6 @@ doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] trio = ["trio (>=0.26.1)"] -[[package]] -name = "astroid" -version = "3.3.10" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.9.0" -groups = ["dev"] -files = [ - {file = "astroid-3.3.10-py3-none-any.whl", hash = "sha256:104fb9cb9b27ea95e847a94c003be03a9e039334a8ebca5ee27dafaf5c5711eb"}, - {file = "astroid-3.3.10.tar.gz", hash = "sha256:c332157953060c6deb9caa57303ae0d20b0fbdb2e59b4a4f2a6ba49d0a7961ce"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "certifi" version = "2025.4.26" @@ -253,7 +191,6 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version < \"3.11\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -262,22 +199,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -[[package]] -name = "click" -version = "8.2.1" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -markers = "python_version >= \"3.11\"" -files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "colorama" version = "0.4.6" @@ -490,22 +411,6 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] -[[package]] -name = "isort" -version = "6.0.1" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.9.0" -groups = ["dev"] -files = [ - {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, - {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, -] - -[package.extras] -colors = ["colorama"] -plugins = ["setuptools"] - [[package]] name = "markdown-it-py" version = "3.0.0" @@ -531,18 +436,6 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -567,18 +460,6 @@ files = [ {file = "mockito-1.5.4.tar.gz", hash = "sha256:f00ed587c32966df3293c294cadb31769460adfc4154f52b90672946aa4b32df"}, ] -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - [[package]] name = "packaging" version = "25.0" @@ -591,35 +472,6 @@ files = [ {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - [[package]] name = "playwright" version = "1.52.0" @@ -825,37 +677,6 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] -[[package]] -name = "pylint" -version = "3.3.7" -description = "python code static checker" -optional = false -python-versions = ">=3.9.0" -groups = ["dev"] -files = [ - {file = "pylint-3.3.7-py3-none-any.whl", hash = "sha256:43860aafefce92fca4cf6b61fe199cdc5ae54ea28f9bf4cd49de267b5195803d"}, - {file = "pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559"}, -] - -[package.dependencies] -astroid = ">=3.3.8,<=3.4.0.dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version == \"3.11\""}, -] -isort = ">=4.2.5,<5.13 || >5.13,<7" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2" -tomli = {version = ">=1.1", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - [[package]] name = "pytest" version = "8.4.0" @@ -957,6 +778,34 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.1 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "ruff" +version = "0.11.13" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46"}, + {file = "ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48"}, + {file = "ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492"}, + {file = "ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250"}, + {file = "ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3"}, + {file = "ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b"}, + {file = "ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514"}, +] + [[package]] name = "shellingham" version = "1.5.4" @@ -1024,18 +873,6 @@ files = [ {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] -[[package]] -name = "tomlkit" -version = "0.13.3" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, - {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, -] - [[package]] name = "typer" version = "0.16.0" @@ -1102,4 +939,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" python-versions = "^3.9" -content-hash = "63b93568937d52951cd6e9b2d49fc02e1dd5d9441615a8f632fc35efd6bf6f40" +content-hash = "4978017e7b7e98d8a9584f0b649091298a41ca3decf5824ad94d86ca2571bac1" diff --git a/pyproject.toml b/pyproject.toml index 6a52294..27cc61d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,21 +17,24 @@ fake-http-header = "^0.3.5" [tool.poetry.group.dev.dependencies] agentql = "^1.3.0" pytest = "^8.3.3" -pylint = "^3.2.7" pytest-mockito = "^0.0.4" dill = "*" -black = "*" +ruff = "*" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[tool.black] -line-length = 100 +[tool.ruff] +line-length = 120 +target-version = "py39" -[tool.isort] -profile = "black" -line_length = 100 -wrap_length = 100 -multi_line_output = 3 -include_trailing_comma = true +[tool.ruff.format] +quote-style = "double" +docstring-code-format = true +docstring-code-line-length = 20 + +[tool.ruff.lint] +select = ["E", "F", "I", "UP"] +ignore = ["E501"] +exclude = ["script/**"] diff --git a/tests/e2e/demo_with_stealth_test.py b/tests/e2e/demo_with_stealth_test.py index b11877b..64a2bc2 100644 --- a/tests/e2e/demo_with_stealth_test.py +++ b/tests/e2e/demo_with_stealth_test.py @@ -1,16 +1,14 @@ import pytest from playwright.sync_api import sync_playwright + from playwright_stealth import stealth_sync -@pytest.mark.skip( - "This test is meant to be ran manually, wrapped under a pytest test so it is not automatically ran." -) +@pytest.mark.skip("This test is meant to be ran manually, wrapped under a pytest test so it is not automatically ran.") def test_demo_with_stealth(): """This test demonstrates how to use playwright-stealth with playwright""" executablePath = "C:\\Google\\Chrome\\Application\\chrome.exe" - ipAndPort = "221.1.90.67:9000" args = [ "--no-sandbox", "--disable-infobars", @@ -43,5 +41,5 @@ def test_demo_with_stealth(): # return None print(f"window navigator webdriver value: {webdriver_flag}") - page.screenshot(path=f"example_with_stealth.png", full_page=True) + page.screenshot(path="example_with_stealth.png", full_page=True) browser.close() diff --git a/tests/e2e/test_all_scripts.py b/tests/e2e/test_all_scripts.py index 0ae11ab..c4dc02a 100644 --- a/tests/e2e/test_all_scripts.py +++ b/tests/e2e/test_all_scripts.py @@ -1,11 +1,16 @@ -import pytest -import agentql -import logging -from playwright.sync_api import sync_playwright, Page as SyncPage -from playwright.async_api import async_playwright, Page as AsyncPage -from tests.utils import from_file import json +import logging + +import agentql +import pytest +from playwright.async_api import Page as AsyncPage +from playwright.async_api import async_playwright +from playwright.sync_api import Page as SyncPage +from playwright.sync_api import sync_playwright + from playwright_stealth.properties import Properties +from tests.utils import from_file + from .configs import ( ScriptConfig, chromeAppConfig, @@ -13,17 +18,7 @@ chromeLoadTimesConfig, chromePluginConfig, chromeRuntimeConfig, - generateMagicArraysConfig, - iFrameContentWindowConfig, - mediaCodecsConfig, - navigatorHardWareConcurrencyConfig, - navigatorLanguagesConfig, - navigatorPermissionsConfig, - navigatorPluginsConfig, navigatorUserAgentConfig, - navigatorVendorConfig, - navigatorWebDriverConfig, - webGLVendorConfig, ) log = logging.getLogger(__name__) @@ -70,9 +65,7 @@ def test_all_scripts_sync(config: ScriptConfig): opts = json.dumps(properties.as_dict()) opts = f"const opts = {opts}" - combined_script = ( - opts + "\n" + utils_script + "\n" + magic_arrays_script + "\n" + config.script - ) + combined_script = opts + "\n" + utils_script + "\n" + magic_arrays_script + "\n" + config.script page.add_init_script(combined_script) page.goto(config.url) @@ -82,9 +75,7 @@ def test_all_scripts_sync(config: ScriptConfig): for key, value in response.items(): assert value == "True" except AssertionError: - page.screenshot( - path=f"tests/e2e/screenshots/{config.name}.png", full_page=True - ) + page.screenshot(path=f"tests/e2e/screenshots/{config.name}.png", full_page=True) raise AssertionError(f"Test failed: {key} is {value}") finally: page.close() @@ -106,9 +97,7 @@ async def test_all_scripts_async(config: ScriptConfig): opts = json.dumps(properties.as_dict()) opts = f"const opts = {opts}" - combined_script = ( - opts + "\n" + utils_script + "\n" + magic_arrays_script + "\n" + config.script - ) + combined_script = opts + "\n" + utils_script + "\n" + magic_arrays_script + "\n" + config.script await page.add_init_script(combined_script) await page.goto(config.url) @@ -118,9 +107,7 @@ async def test_all_scripts_async(config: ScriptConfig): for key, value in response.items(): assert value == "True" except AssertionError: - await page.screenshot( - path=f"tests/e2e/screenshots/{config.name}.png", full_page=True - ) + await page.screenshot(path=f"tests/e2e/screenshots/{config.name}.png", full_page=True) raise AssertionError(f"Test failed: {key} is {value}") finally: await page.close() diff --git a/tests/e2e/test_multiple_scripts.py b/tests/e2e/test_multiple_scripts.py index 2462fc5..60d2a93 100644 --- a/tests/e2e/test_multiple_scripts.py +++ b/tests/e2e/test_multiple_scripts.py @@ -1,8 +1,11 @@ -import pytest -import agentql import logging -from playwright.sync_api import sync_playwright, Page as SyncPage -from playwright.async_api import async_playwright, Page as AsyncPage + +import agentql +import pytest +from playwright.async_api import Page as AsyncPage +from playwright.async_api import async_playwright +from playwright.sync_api import sync_playwright + from .configs import ( MultipleScriptConfig, chromeAppConfig, @@ -29,11 +32,7 @@ def extract_query_lines(query_str) -> list[str]: """This function is used to extract the query lines from the query string""" lines = query_str.strip().splitlines() - return [ - line.strip() - for line in lines - if line.strip() and not line.startswith("{") and not line.endswith("}") - ] + return [line.strip() for line in lines if line.strip() and not line.startswith("{") and not line.endswith("}")] def join_query_lines(query_lines) -> str: @@ -107,7 +106,7 @@ def test_multiple_scripts(multiple_configs: MultipleScriptConfig): @pytest.mark.asyncio -async def test_multiple_scripts(multiple_configs: MultipleScriptConfig): +async def test_multiple_scripts_async(multiple_configs: MultipleScriptConfig): async with async_playwright() as p: browser = await p.chromium.launch(headless=True) page: AsyncPage = await agentql.wrap_async(browser.new_page()) diff --git a/tests/e2e/test_one_script.py b/tests/e2e/test_one_script.py index 3c1d872..fbad35a 100644 --- a/tests/e2e/test_one_script.py +++ b/tests/e2e/test_one_script.py @@ -1,26 +1,14 @@ -import pytest -import agentql import logging -from playwright.sync_api import sync_playwright, Page as SyncPage -from playwright.async_api import async_playwright, Page as AsyncPage + +import agentql +import pytest +from playwright.async_api import Page as AsyncPage +from playwright.async_api import async_playwright +from playwright.sync_api import sync_playwright + from .configs import ( ScriptConfig, chromeAppConfig, - chromeCsiConfig, - chromeLoadTimesConfig, - chromePluginConfig, - chromeRuntimeConfig, - generateMagicArraysConfig, - iFrameContentWindowConfig, - mediaCodecsConfig, - navigatorHardWareConcurrencyConfig, - navigatorLanguagesConfig, - navigatorPermissionsConfig, - navigatorPluginsConfig, - navigatorUserAgentConfig, - navigatorVendorConfig, - navigatorWebDriverConfig, - webGLVendorConfig, ) log = logging.getLogger(__name__) diff --git a/tests/unit/header_properties_test.py b/tests/unit/header_properties_test.py index 8ed563f..9d1c372 100644 --- a/tests/unit/header_properties_test.py +++ b/tests/unit/header_properties_test.py @@ -1,5 +1,6 @@ import pytest from fake_http_header import FakeHttpHeader + from playwright_stealth.properties._header_properties import HeaderProperties @@ -30,18 +31,9 @@ def test_initialization(header_properties, fake_headers, brands): assert header_properties.referer == fake_headers["Referer"] assert header_properties.dnt == "1" assert header_properties.sec_ch_ua == header_properties._generate_sec_ch_ua(brands) - assert ( - header_properties.sec_ch_ua_mobile - == header_properties._generate_sec_ch_ua_mobile() - ) - assert ( - header_properties.sec_ch_ua_platform - == header_properties._generate_sec_ch_ua_platform() - ) - assert ( - header_properties.sec_ch_ua_form_factors - == header_properties._generate_sec_ch_ua_form_factors() - ) + assert header_properties.sec_ch_ua_mobile == header_properties._generate_sec_ch_ua_mobile() + assert header_properties.sec_ch_ua_platform == header_properties._generate_sec_ch_ua_platform() + assert header_properties.sec_ch_ua_form_factors == header_properties._generate_sec_ch_ua_form_factors() def test_generate_sec_ch_ua_platform(header_properties): @@ -99,10 +91,7 @@ def test_as_dict(header_properties): assert headers_dict["sec-ch-ua"] == header_properties.sec_ch_ua assert headers_dict["sec-ch-ua-mobile"] == header_properties.sec_ch_ua_mobile assert headers_dict["sec-ch-ua-platform"] == header_properties.sec_ch_ua_platform - assert ( - headers_dict["sec-ch-ua-form-factors"] - == header_properties.sec_ch_ua_form_factors - ) + assert headers_dict["sec-ch-ua-form-factors"] == header_properties.sec_ch_ua_form_factors @pytest.mark.parametrize( @@ -125,7 +114,7 @@ def test_generate_sec_ch_ua_platform_parametrized(user_agent, expected_platform) "Accept-encoding": "", "Accept": "", "Referer": "", - } + }, ) assert header_properties._generate_sec_ch_ua_platform() == expected_platform diff --git a/tests/unit/navigator_properties_test.py b/tests/unit/navigator_properties_test.py index aa45c3a..377f9a9 100644 --- a/tests/unit/navigator_properties_test.py +++ b/tests/unit/navigator_properties_test.py @@ -1,5 +1,6 @@ import pytest from fake_http_header import FakeHttpHeader + from playwright_stealth.properties._navigator_properties import NavigatorProperties @@ -30,10 +31,7 @@ def test_initialization(navigator_properties, fake_headers, brands): assert navigator_properties.platform in ["MacIntel", "Win64", "Linux x86_x64"] assert navigator_properties.language == "en-US" assert isinstance(navigator_properties.languages, list) - assert ( - navigator_properties.appVersion - == navigator_properties._generate_app_version(fake_headers["User-Agent"]) - ) + assert navigator_properties.appVersion == navigator_properties._generate_app_version(fake_headers["User-Agent"]) assert navigator_properties.vendor in ["Google Inc.", ""] assert navigator_properties.deviceMemory == 8 assert navigator_properties.hardwareConcurrency == 8 @@ -57,9 +55,7 @@ def test_generate_languages(navigator_properties): """Test the _generate_languages method.""" accept_language = "en-US,en;q=0.9,fr;q=0.8" expected_languages = ["en-US", "en", "fr"] - assert ( - navigator_properties._generate_languages(accept_language) == expected_languages - ) + assert navigator_properties._generate_languages(accept_language) == expected_languages def test_generate_app_version(navigator_properties): @@ -89,10 +85,7 @@ def test_as_dict(navigator_properties): assert navigator_dict["appVersion"] == navigator_properties.appVersion assert navigator_dict["vendor"] == navigator_properties.vendor assert navigator_dict["deviceMemory"] == navigator_properties.deviceMemory - assert ( - navigator_dict["hardwareConcurrency"] - == navigator_properties.hardwareConcurrency - ) + assert navigator_dict["hardwareConcurrency"] == navigator_properties.hardwareConcurrency assert navigator_dict["maxTouchPoints"] == navigator_properties.maxTouchPoints assert navigator_dict["doNotTrack"] == navigator_properties.doNotTrack assert navigator_dict["brands"] == navigator_properties.brands diff --git a/tests/unit/properties_test.py b/tests/unit/properties_test.py index 6162de2..6d6aa23 100644 --- a/tests/unit/properties_test.py +++ b/tests/unit/properties_test.py @@ -1,13 +1,15 @@ import logging + import pytest -from mockito import when, when2, unstub, mock from fake_http_header import FakeHttpHeader -from playwright_stealth.properties._navigator_properties import NavigatorProperties +from mockito import mock, unstub, when, when2 + +import playwright_stealth.properties._properties as properties from playwright_stealth.properties._header_properties import HeaderProperties +from playwright_stealth.properties._navigator_properties import NavigatorProperties +from playwright_stealth.properties._properties import Properties from playwright_stealth.properties._viewport_properties import ViewportProperties from playwright_stealth.properties._webgl_properties import WebGlProperties -from playwright_stealth.properties._properties import Properties -import playwright_stealth.properties._properties as properties log = logging.getLogger(__name__) @@ -28,9 +30,7 @@ def fake_headers(): def mock_fake_http_header(fake_headers): """Mocks FakeHttpHeader used within the Properties class.""" mock_instance = mock(FakeHttpHeader) - when2(properties.FakeHttpHeader, domain_code="com", browser="chrome").thenReturn( - mock_instance - ) + when2(properties.FakeHttpHeader, domain_code="com", browser="chrome").thenReturn(mock_instance) mock_instance.user_agent = fake_headers["User-Agent"] when(mock_instance).as_header_dict().thenReturn(fake_headers) yield diff --git a/tests/unit/viewport_properties_test.py b/tests/unit/viewport_properties_test.py index 773d4af..741045e 100644 --- a/tests/unit/viewport_properties_test.py +++ b/tests/unit/viewport_properties_test.py @@ -1,6 +1,7 @@ -import pytest -from mockito import when, unstub import random + +from mockito import unstub, when + from playwright_stealth.properties._viewport_properties import ViewportProperties @@ -95,9 +96,7 @@ def test_randomness_in_dimensions(): viewport2.innerHeight, ) # Since randomness is involved, dimensions may occasionally be the same, which is acceptable - assert ( - dimensions1 != dimensions2 or True - ) # Accept possible equality due to randomness + assert dimensions1 != dimensions2 or True # Accept possible equality due to randomness def test_custom_kwargs(): diff --git a/tests/unit/webgl_properties_test.py b/tests/unit/webgl_properties_test.py index 69b24e4..6d404b6 100644 --- a/tests/unit/webgl_properties_test.py +++ b/tests/unit/webgl_properties_test.py @@ -1,6 +1,7 @@ -import pytest -from mockito import when, unstub import random + +from mockito import unstub, when + from playwright_stealth.properties._webgl_properties import WebGlProperties @@ -38,9 +39,7 @@ def test_randomness_in_properties(): webgl2 = WebGlProperties() # Since randomness is involved, properties may sometimes be the same # This test checks that the class can produce different properties - assert ( - (webgl1.vendor != webgl2.vendor) or (webgl1.renderer != webgl2.renderer) or True - ) + assert (webgl1.vendor != webgl2.vendor) or (webgl1.renderer != webgl2.renderer) or True def test_all_possible_properties(): @@ -49,7 +48,5 @@ def test_all_possible_properties(): for _ in range(100): webgl = WebGlProperties() generated_props.add((webgl.vendor, webgl.renderer)) - expected_props = { - (prop["vendor"], prop["renderer"]) for prop in webgl.webgl_properties - } + expected_props = {(prop["vendor"], prop["renderer"]) for prop in webgl.webgl_properties} assert generated_props == expected_props