From 44571c22ecf4c4e7f6b7e4458c05a542aa817f7b Mon Sep 17 00:00:00 2001 From: Vishal Kumar Mishra Date: Thu, 14 Sep 2023 17:19:31 +0530 Subject: [PATCH] Browser Query Initial Commit --- .editorconfig | 27 +++++ .flake8 | 3 + .github/workflows/python-publish.yml | 31 +++++ .gitignore | 140 +++++++++++++++++++++ .isort.cfg | 7 ++ .pre-commit-config.yaml | 67 ++++++++++ .sourcery.yaml | 41 +++++++ LICENSE | 21 ++++ README.md | 3 + bandit.yaml | 10 ++ browserjquery/__init__.py | 0 browserjquery/data/jquery.js | 2 + browserjquery/jquery.py | 148 +++++++++++++++++++++++ browserjquery/jquery_scripts.py | 18 +++ browserjquery/log_config/log_config.yaml | 21 ++++ browserjquery/settings.py | 53 ++++++++ browserjquery/tests/__init__.py | 0 browserjquery/tests/test_basic.py | 17 +++ example.env | 10 ++ pyproject.toml | 111 +++++++++++++++++ pytest.ini | 4 + requirements/base.txt | 6 + requirements/dev.txt | 12 ++ tox.ini | 11 ++ utility_scripts/bandit_util.sh | 5 + utility_scripts/black_util.sh | 4 + utility_scripts/codelimit_util.sh | 3 + utility_scripts/mypy_util.sh | 6 + utility_scripts/pre_commit_util.sh | 7 ++ utility_scripts/ruff_util.sh | 7 ++ utility_scripts/sourcery_utils.sh | 8 ++ 31 files changed, 803 insertions(+) create mode 100644 .editorconfig create mode 100755 .flake8 create mode 100644 .github/workflows/python-publish.yml create mode 100755 .gitignore create mode 100755 .isort.cfg create mode 100644 .pre-commit-config.yaml create mode 100644 .sourcery.yaml create mode 100755 LICENSE create mode 100755 README.md create mode 100644 bandit.yaml create mode 100644 browserjquery/__init__.py create mode 100644 browserjquery/data/jquery.js create mode 100644 browserjquery/jquery.py create mode 100644 browserjquery/jquery_scripts.py create mode 100644 browserjquery/log_config/log_config.yaml create mode 100644 browserjquery/settings.py create mode 100644 browserjquery/tests/__init__.py create mode 100644 browserjquery/tests/test_basic.py create mode 100755 example.env create mode 100755 pyproject.toml create mode 100644 pytest.ini create mode 100644 requirements/base.txt create mode 100644 requirements/dev.txt create mode 100644 tox.ini create mode 100644 utility_scripts/bandit_util.sh create mode 100644 utility_scripts/black_util.sh create mode 100644 utility_scripts/codelimit_util.sh create mode 100644 utility_scripts/mypy_util.sh create mode 100644 utility_scripts/pre_commit_util.sh create mode 100644 utility_scripts/ruff_util.sh create mode 100644 utility_scripts/sourcery_utils.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2614070 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{py,rst,ini}] +indent_style = space +indent_size = 4 + +[*.{html,css,scss,json,yml}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab + +[nginx.conf] +indent_style = space +indent_size = 2 diff --git a/.flake8 b/.flake8 new file mode 100755 index 0000000..142efb8 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 120 +exclude = .tox,.git,venv diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..b88709e --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,31 @@ +name: Upload Python Package + +on: + push: + branches: + - main + +permissions: + contents: read + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements/dev.txt + - name: Set PYPI token + run: poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }} + - name: Build package + run: poetry build + - name: Publish package + run: poetry publish diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..9227116 --- /dev/null +++ b/.gitignore @@ -0,0 +1,140 @@ +.idea/ +linkedin_old.py +test.html +.DS_Store + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +.tox +*.log +.env +.cron.env +todos.md diff --git a/.isort.cfg b/.isort.cfg new file mode 100755 index 0000000..efaf533 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,7 @@ +[settings] +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +use_parentheses = True +ensure_newline_before_comments = True +line_length = 120 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ba54482 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,67 @@ +exclude: '^docs/|/migrations/' +default_stages: [ commit ] +fail_fast: false + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-json + - id: check-toml + - id: check-xml + - id: check-yaml + - id: debug-statements + - id: check-builtin-literals + - id: check-case-conflict + - id: check-docstring-first + - id: detect-private-key + + - repo: https://github.com/asottile/pyupgrade + rev: v3.7.0 + hooks: + - id: pyupgrade + args: [ --py311-plus ] + + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.280 + hooks: + - id: ruff + args: [ --fix, --exit-non-zero-on-fix ] + types_or: [ python, pyi, jupyter ] + + - repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black + + - repo: https://github.com/PyCQA/bandit + rev: 1.7.5 + hooks: + - id: bandit + args: [ "-c", "bandit.yaml" ] + + - repo: https://github.com/sourcery-ai/sourcery + # Source: https://docs.sourcery.ai/Guides/Getting-Started/Pre-Commit/ + # https://sourcery.ai/blog/python-best-practices/ + rev: v1.6.0 + hooks: + - id: sourcery + # only check the files which have changed: does not work on first commit + # args: [ --diff=git diff HEAD, --no-summary ] + # args: [ --diff=git diff HEAD, --fix, --no-summary ] + args: [ --fix, --no-summary ] + + +# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date +ci: + autoupdate_schedule: weekly + skip: [ ] + submodules: false diff --git a/.sourcery.yaml b/.sourcery.yaml new file mode 100644 index 0000000..3702439 --- /dev/null +++ b/.sourcery.yaml @@ -0,0 +1,41 @@ +#https://docs.sourcery.ai/Reference/Configuration/sourcery-yaml/ + +ignore: + - .git + - venv + - .venv + - env + - .env + - .tox + - node_modules + - vendor + - repository_directory + +rule_settings: + enable: [default] + disable: [] + rule_types: + - refactoring + - suggestion + - comment + python_version: '3.11' + +rules: [] + +metrics: + quality_threshold: 25.0 + +github: + labels: [] + ignore_labels: + - sourcery-ignore + request_review: author + sourcery_branch: sourcery/{base_branch} + +clone_detection: + min_lines: 3 + min_duplicates: 2 + identical_clones_only: false + +proxy: + no_ssl_verify: false diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..53a6936 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-2023 Vishal Kumar Mishra + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..44f0e56 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# BrowserQuery + +Use JQuery on selenium drivers. diff --git a/bandit.yaml b/bandit.yaml new file mode 100644 index 0000000..2ad0c88 --- /dev/null +++ b/bandit.yaml @@ -0,0 +1,10 @@ +# also see pyproject.toml for [tool.bandit] +# https://bandit.readthedocs.io/en/1.7.5/config.html +# exclude = tests,path/to/file +# tests = B201,B301 + +exclude_dirs: [ 'tests/', 'repository_directory/', "tox" ] +tests: [ ] +skips: [ + 'B113' # request.get without a timeout +] diff --git a/browserjquery/__init__.py b/browserjquery/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/browserjquery/data/jquery.js b/browserjquery/data/jquery.js new file mode 100644 index 0000000..35906b9 --- /dev/null +++ b/browserjquery/data/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},m=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||m).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),b=new RegExp(ge+"|>"),A=new RegExp(g),D=new RegExp("^"+t+"$"),N={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+d),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},L=/^(?:input|select|textarea|button)$/i,j=/^h\d$/i,O=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,P=/[+~]/,H=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),q=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},R=function(){V()},M=K(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{E.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){E={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&(V(e),e=e||C,T)){if(11!==d&&(u=O.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return E.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return E.call(n,a),n}else{if(u[2])return E.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return E.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||p&&p.test(t))){if(c=t,f=e,1===d&&(b.test(t)||m.test(t))){(f=P.test(t)&&X(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=k)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+G(l[o]);c=l.join(",")}try{return E.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function B(e){return e[k]=!0,e}function F(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function $(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&M(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function U(a){return B(function(o){return o=+o,B(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function X(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=C&&9===n.nodeType&&n.documentElement&&(r=(C=n).documentElement,T=!ce.isXMLDoc(C),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=C&&(t=C.defaultView)&&t.top!==t&&t.addEventListener("unload",R),le.getById=F(function(e){return r.appendChild(e).id=ce.expando,!C.getElementsByName||!C.getElementsByName(ce.expando).length}),le.disconnectedMatch=F(function(e){return i.call(e,"*")}),le.scope=F(function(){return C.querySelectorAll(":scope")}),le.cssHas=F(function(){try{return C.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(x.filter.ID=function(e){var t=e.replace(H,q);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&T){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(H,q);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&T){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},x.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&T)return t.getElementsByClassName(e)},p=[],F(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||p.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+k+"-]").length||p.push("~="),e.querySelectorAll("a#"+k+"+*").length||p.push(".#.+[+~]"),e.querySelectorAll(":checked").length||p.push(":checked"),(t=C.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&p.push(":enabled",":disabled"),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||p.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||p.push(":has"),p=p.length&&new RegExp(p.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===C||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),C}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),T&&!h[t+" "]&&(!p||!p.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(H,q),e[3]=(e[3]||e[4]||e[5]||"").replace(H,q),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return N.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&A.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(H,q).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||E,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:k.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:m,!0)),C.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=m.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,E=ce(m);var S=/^(?:parents|prev(?:Until|All))/,A={children:!0,contents:!0,next:!0,prev:!0};function D(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;re=m.createDocumentFragment().appendChild(m.createElement("div")),(be=m.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),re.appendChild(be),le.checkClone=re.cloneNode(!0).cloneNode(!0).lastChild.checked,re.innerHTML="",le.noCloneChecked=!!re.cloneNode(!0).lastChild.defaultValue,re.innerHTML="",le.option=!!re.lastChild;var Te={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Ee(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function ke(e,t){for(var n=0,r=e.length;n",""]);var Se=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),d=[],p=0,h=e.length;p\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Me(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ie(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function We(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n
",2===yt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=m.implementation.createHTMLDocument("")).createElement("base")).href=m.location.href,t.head.appendChild(r)):t=m),o=!n&&[],(i=C.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||K})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return R(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Qe(le.pixelPosition,function(e,t){if(t)return t=Ve(e,n),$e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return R(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0 str: + # https://stackoverflow.com/a/982742/8414030 + + return self.execute(jquery_scripts.PAGE_HTML) + + def inject_jquery(self, by: str = "file", wait: int = 5) -> bool: + """ + SO: https://stackoverflow.com/a/57947790/8414030 + """ + logger.info("Jquery being injected.") + + self._inject_jquery_file(wait=wait) if by == "file" else self._inject_jquery_cdn(wait=wait) + + return self.is_jquery_injected + + def _inject_jquery_cdn(self, wait: int = 2): + self.driver.execute_script(jquery_scripts.JQUERY_INJECTION) + + time.sleep(wait) + + def _inject_jquery_file(self, wait: int = 5): + with open(settings.JQUERY_INJECTION_FILE) as f: + self.execute(f.read()) + + time.sleep(wait) + + @property + def is_jquery_injected(self) -> bool: + with contextlib.suppress(Exception): + self.execute(jquery_scripts.JQUERY_INJECTION_CHECK) + return True + + return False + + @staticmethod + def _prepare_result(result, first: bool): + if isinstance(result, list): + if first: + return result[0] if result else None + return result + + return result + + def find( + self, selector: str, element: webelement.WebElement = None, *, first_match: bool = False + ) -> list[webelement.WebElement] | webelement.WebElement | None: + """Find elements using the given element or entire dom based on selector""" + + first_match = first_match or selector.startswith("#") + element = element or self.document + method = ".first()" if first_match else "" + + return self._prepare_result( + self.query( + script=f"""return $(arguments[0]).find("{selector}"){method};""", + element=element, + ), + first=first_match, + ) + + def find_elements_with_text( + self, text: str, selector: str = "*", element: webelement.WebElement = None, *, first_match: bool = False + ) -> list[webelement.WebElement] | webelement.WebElement | None: + """Find all elements with given texts""" + + method = ".first()" if first_match else "" + + return self._prepare_result( + self.query( + script=f""" + return $(arguments[0]).find("{selector}:contains('{text}')"){method}; + """, + element=element or self.document, + ), + first=first_match, + ) + + def find_closest_ancestor(self, selector: str, element: webelement.WebElement) -> webelement.WebElement | None: + """Find the closest ancestor with given selector properties""" + + return self._prepare_result( + self.query(script=f"""return $(arguments[0]).closest('{selector}')""", element=element), first=True + ) + + def has_class(self, element: webelement.WebElement, class_name: str) -> bool: + return self.query( + script=f""" + return $(arguments[0]).hasClass('{class_name}') + """, + element=element, + ) + + def parent(self, element: webelement.WebElement) -> webelement.WebElement: + return self.query( + script=""" + return $(arguments[0]).parent()') + """, + element=element, + ) + + def parents(self, element: webelement.WebElement) -> list[webelement.WebElement]: + return self.query( + script=""" + return $(arguments[0]).parents()') + """, + element=element, + ) diff --git a/browserjquery/jquery_scripts.py b/browserjquery/jquery_scripts.py new file mode 100644 index 0000000..497309f --- /dev/null +++ b/browserjquery/jquery_scripts.py @@ -0,0 +1,18 @@ +JQUERY_INJECTION = """ + var script = document.createElement( 'script' ); + script.type = 'text/javascript'; + script.src = 'https://code.jquery.com/jquery-3.7.1.min.js'; + document.head.appendChild(script); + + script.onload = function() { + var $ = window.jQuery; + } +""" + +PAGE_HTML = """ + return "" + $("html").html() + ""; +""" + +JQUERY_INJECTION_CHECK = """ + return $ +""" diff --git a/browserjquery/log_config/log_config.yaml b/browserjquery/log_config/log_config.yaml new file mode 100644 index 0000000..745ce01 --- /dev/null +++ b/browserjquery/log_config/log_config.yaml @@ -0,0 +1,21 @@ +# YAML copied from: https://realpython.com/python-logging/#other-configuration-methods +version: 1 +formatters: + extended: + format: '%(asctime)-20s :: %(levelname)-8s :: [%(process)d]%(processName)s :: %(threadName)s[%(thread)d] :: %(pathname)s :: %(lineno)d :: %(message)s' + simple: + format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' +handlers: + console: + class: logging.StreamHandler + level: DEBUG + formatter: simple + stream: ext://sys.stdout +loggers: + sampleLogger: + level: DEBUG + handlers: [console] + propagate: no +root: + level: DEBUG + handlers: [console] diff --git a/browserjquery/settings.py b/browserjquery/settings.py new file mode 100644 index 0000000..7cabb67 --- /dev/null +++ b/browserjquery/settings.py @@ -0,0 +1,53 @@ +# https://github.com/pjialin/django-environ + +import logging.config +from pathlib import Path + +import environ +import yaml + +BASE_DIR = Path(__file__).parent + +LOG_CONF_FILE = "log_config/log_config.yaml" +ENV_FILE = "../.env" + +# ======== +# LOGGING +# ======== + +# Usage: +# from settings import get_logger +# logger = get_logger(__name__) +# logger.log("message") + +with open(BASE_DIR / LOG_CONF_FILE) as f: + config = yaml.safe_load(f.read()) + logging.config.dictConfig(config) + + +def getLogger(name): # noqa + return logging.getLogger(name) + + +get_logger = getLogger + +# =========================== +# ENVIRONMENT VARIABLE UTILS +# =========================== + +env = environ.Env() + +env_file = BASE_DIR / Path(ENV_FILE) +env.read_env(env_file=env_file) + +# ============================ +# GLOBAL ENVIRONMENT VARIABLES +# ============================ + +# ENV_VAR = env("ENV_VAR") + +# ============================ +# JQUERY INJECTION FILE +# ============================ + +JQUERY_INJECTION_FILE = BASE_DIR / "data" / "jquery.js" diff --git a/browserjquery/tests/__init__.py b/browserjquery/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/browserjquery/tests/test_basic.py b/browserjquery/tests/test_basic.py new file mode 100644 index 0000000..41aa89f --- /dev/null +++ b/browserjquery/tests/test_basic.py @@ -0,0 +1,17 @@ +import time + +from selenium.webdriver import Chrome + +from browserjquery.jquery import BrowserJQuery + + +def test_basic_function(): + driver = Chrome() + + driver.get("https://www.yahoo.com") + + time.sleep(10) + + jquery = BrowserJQuery(driver) + + assert jquery(".stream-item"), "BrowserQuery is not working" diff --git a/example.env b/example.env new file mode 100755 index 0000000..5f9bb38 --- /dev/null +++ b/example.env @@ -0,0 +1,10 @@ +LINKEDIN_USER= +LINKEDIN_PASSWORD= +LINKEDIN_BROWSER=Chrome +LINKEDIN_BROWSER_HEADLESS=1 +LINKEDIN_PREFERRED_USER=data/users_preferred.txt +LINKEDIN_NOT_PREFERRED_USER=data/users_not_preferred.txt +LINKEDIN_MIN_MUTUAL=0 +LINKEDIN_MAX_MUTUAL=500 +LINKEDIN_MAX_INVITE=0 +LINKEDIN_WITHDRAW_INVITE_BEFORE_DAYS=14 diff --git a/pyproject.toml b/pyproject.toml new file mode 100755 index 0000000..42cab94 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,111 @@ +[tool.black] +line-length = 120 +target-version = ['py311'] +include = '\.pyi?$' +exclude = ''' +/( + \.toml + |\.sh + |\.git + |\.ini + |\.tox + |Dockerfile + |Jenkinfile + | _build + | dist +)/ +''' + +# ==== isort ==== +[tool.isort] +profile = "black" +line_length = 120 +known_first_party = [ + "config", + "apps", +] +skip = [] +skip_glob = [] + +# ==== pytest ==== +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "--ds=config.settings --reuse-db" +python_files = [ + "tests.py", + "test_*.py", +] + +# ==== mypy ==== +[tool.mypy] +python_version = "3.11" +check_untyped_defs = true +ignore_missing_imports = true +warn_unused_ignores = true +warn_redundant_casts = true +warn_unused_configs = true + + +[tool.ruff] +# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. +select = ["E", "F"] +ignore = [] + +# Allow autofix for all enabled rules (when `--fix`) is provided. +fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"] +unfixable = [] + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] + +line-length = 120 +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +target-version = "py310" + +[tool.ruff.mccabe] +# Unlike Flake8, default to a complexity level of 10. +max-complexity = 10 + +[tool.bandit] +exclude_dirs = [".tox"] +# tests = ["B201", "B301"] +skips = ["B113"] + +[tool.poetry] +name = "browserquery" +version = "0.1.0" +description = "Query using Jquery on selenium driver." +authors = ["Vishal Kumar Mishra "] +keywords = ["selenium", "selenium python", "browserquery", "python jquery"] +license = "MIT" +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..fa044b6 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +env = + LINKEDIN_USER= + LINKEDIN_PASSWORD= diff --git a/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..ed1fd88 --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,6 @@ +selenium>=4.12.0 + +django-environ +python-dotenv + +PyYAML diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 0000000..519e8f9 --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,12 @@ +-r base.txt + +# Development related +pre-commit +black +ruff +sourcery +mypy +bandit +tox +pytest +poetry diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..2c4aaa1 --- /dev/null +++ b/tox.ini @@ -0,0 +1,11 @@ +[tox] +;skipsdist = True +isolated_build = True +envlist = py310 + +[testenv] +whitelist_externals = poetry +allowlist_externals = poetry +commands = + poetry install -v + poetry run pytest diff --git a/utility_scripts/bandit_util.sh b/utility_scripts/bandit_util.sh new file mode 100644 index 0000000..df4a85a --- /dev/null +++ b/utility_scripts/bandit_util.sh @@ -0,0 +1,5 @@ +( + cd .. + bandit -c bandit.yaml -r $PWD + exit 0 +) diff --git a/utility_scripts/black_util.sh b/utility_scripts/black_util.sh new file mode 100644 index 0000000..9c6b225 --- /dev/null +++ b/utility_scripts/black_util.sh @@ -0,0 +1,4 @@ +( + cd .. + black . +) diff --git a/utility_scripts/codelimit_util.sh b/utility_scripts/codelimit_util.sh new file mode 100644 index 0000000..fd01374 --- /dev/null +++ b/utility_scripts/codelimit_util.sh @@ -0,0 +1,3 @@ +# https://github.com/getcodelimit/codelimit + +codelimit check $PWD diff --git a/utility_scripts/mypy_util.sh b/utility_scripts/mypy_util.sh new file mode 100644 index 0000000..e0be498 --- /dev/null +++ b/utility_scripts/mypy_util.sh @@ -0,0 +1,6 @@ +( + cd .. + + mypy . + +) diff --git a/utility_scripts/pre_commit_util.sh b/utility_scripts/pre_commit_util.sh new file mode 100644 index 0000000..736aeed --- /dev/null +++ b/utility_scripts/pre_commit_util.sh @@ -0,0 +1,7 @@ +( + cd .. + + pre-commit run --all-files + + exit 0 +) diff --git a/utility_scripts/ruff_util.sh b/utility_scripts/ruff_util.sh new file mode 100644 index 0000000..9d4b282 --- /dev/null +++ b/utility_scripts/ruff_util.sh @@ -0,0 +1,7 @@ +( + cd .. + + # ruff -h + + ruff . --fix +) diff --git a/utility_scripts/sourcery_utils.sh b/utility_scripts/sourcery_utils.sh new file mode 100644 index 0000000..e3dc889 --- /dev/null +++ b/utility_scripts/sourcery_utils.sh @@ -0,0 +1,8 @@ +# https://docs.sourcery.ai/Guides/Getting-Started/Command-Line/ +# https://docs.sourcery.ai/Reference/Configuration/Inline/#skip-a-specific-refactoring + +( + cd .. + + sourcery review . +)