diff options
| author | Árni Dagur <agudmundsson@fc-md.umd.edu> | 2020-03-28 23:15:47 -0400 |
|---|---|---|
| committer | Árni Dagur <agudmundsson@fc-md.umd.edu> | 2020-03-28 23:15:47 -0400 |
| commit | 129bfc10146973ce8a33fcd383c56c4f8071a492 (patch) | |
| tree | 453a3a4f4776e29859df94c4a97354940e3dc919 | |
first commit
| -rw-r--r-- | .github/workflows/ci.yml | 72 | ||||
| -rw-r--r-- | .gitignore | 243 | ||||
| -rw-r--r-- | Cargo.toml | 18 | ||||
| -rw-r--r-- | pyproject.toml | 15 | ||||
| -rw-r--r-- | src/lib.rs | 181 |
5 files changed, 529 insertions, 0 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..24f06e1 --- /dev/null +++ b/.github/workflows/ci.yml | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | name: CI | ||
| 2 | |||
| 3 | on: | ||
| 4 | release: | ||
| 5 | types: [created] | ||
| 6 | push: | ||
| 7 | schedule: | ||
| 8 | # Runs every Thursday at 20:23 GMT to avoid bit rot | ||
| 9 | - cron: "20 23 * * 4" | ||
| 10 | |||
| 11 | jobs: | ||
| 12 | lint: | ||
| 13 | runs-on: ubuntu-latest | ||
| 14 | steps: | ||
| 15 | - name: Checkout | ||
| 16 | uses: actions/checkout@v1 | ||
| 17 | |||
| 18 | - name: Install latest nightly | ||
| 19 | uses: actions-rs/toolchain@v1 | ||
| 20 | with: | ||
| 21 | toolchain: nightly | ||
| 22 | override: true | ||
| 23 | |||
| 24 | - name: Lint with rustfmt | ||
| 25 | uses: actions-rs/cargo@v1 | ||
| 26 | with: | ||
| 27 | command: fmt | ||
| 28 | |||
| 29 | - name: Lint with clippy | ||
| 30 | uses: actions-rs/cargo@v1 | ||
| 31 | with: | ||
| 32 | command: clippy | ||
| 33 | args: --all-targets --all-features | ||
| 34 | |||
| 35 | build: | ||
| 36 | runs-on: ${{ matrix.os }} | ||
| 37 | needs: lint | ||
| 38 | strategy: | ||
| 39 | fail-fast: false | ||
| 40 | matrix: | ||
| 41 | python-version: [3.5, 3.6, 3.7, 3.8] | ||
| 42 | os: [ubuntu-latest, macos-latest, windows-latest] | ||
| 43 | exclude: | ||
| 44 | # There is a known issue where Python C extensions | ||
| 45 | # can not be built on Python 3.5 on Windows. | ||
| 46 | # https://github.com/actions/virtual-environments/issues/34 | ||
| 47 | # Therefore, ujson can't be compiled and the build would fail. | ||
| 48 | - os: windows-latest | ||
| 49 | python-version: 3.5 | ||
| 50 | |||
| 51 | steps: | ||
| 52 | - name: Checkout | ||
| 53 | uses: actions/checkout@v1 | ||
| 54 | |||
| 55 | - name: Set up Python ${{ matrix.python-version }} | ||
| 56 | uses: actions/setup-python@v1 | ||
| 57 | with: | ||
| 58 | python-version: ${{ matrix.python-version }} | ||
| 59 | |||
| 60 | - name: Install dependencies | ||
| 61 | run: | | ||
| 62 | python -m pip install --upgrade pip | ||
| 63 | pip install poetry | ||
| 64 | poetry install | ||
| 65 | - name: Install latest nightly | ||
| 66 | uses: actions-rs/toolchain@v1 | ||
| 67 | with: | ||
| 68 | toolchain: nightly | ||
| 69 | override: true | ||
| 70 | |||
| 71 | - name: Build Python package | ||
| 72 | run: poetry run maturin develop --release \ No newline at end of file | ||
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb366a8 --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1,243 @@ | |||
| 1 | # -- Rust -- | ||
| 2 | /target | ||
| 3 | Cargo.lock | ||
| 4 | |||
| 5 | # -- Editors -- | ||
| 6 | # Created by https://www.gitignore.io/api/vim,emacs,visualstudiocode | ||
| 7 | # Edit at https://www.gitignore.io/?templates=vim,emacs,visualstudiocode | ||
| 8 | |||
| 9 | ### Emacs ### | ||
| 10 | # -*- mode: gitignore; -*- | ||
| 11 | *~ | ||
| 12 | \#*\# | ||
| 13 | /.emacs.desktop | ||
| 14 | /.emacs.desktop.lock | ||
| 15 | *.elc | ||
| 16 | auto-save-list | ||
| 17 | tramp | ||
| 18 | .\#* | ||
| 19 | |||
| 20 | # Org-mode | ||
| 21 | .org-id-locations | ||
| 22 | *_archive | ||
| 23 | |||
| 24 | # flymake-mode | ||
| 25 | *_flymake.* | ||
| 26 | |||
| 27 | # eshell files | ||
| 28 | /eshell/history | ||
| 29 | /eshell/lastdir | ||
| 30 | |||
| 31 | # elpa packages | ||
| 32 | /elpa/ | ||
| 33 | |||
| 34 | # reftex files | ||
| 35 | *.rel | ||
| 36 | |||
| 37 | # AUCTeX auto folder | ||
| 38 | /auto/ | ||
| 39 | |||
| 40 | # cask packages | ||
| 41 | .cask/ | ||
| 42 | dist/ | ||
| 43 | |||
| 44 | # Flycheck | ||
| 45 | flycheck_*.el | ||
| 46 | |||
| 47 | # server auth directory | ||
| 48 | /server/ | ||
| 49 | |||
| 50 | # projectiles files | ||
| 51 | .projectile | ||
| 52 | |||
| 53 | # directory configuration | ||
| 54 | .dir-locals.el | ||
| 55 | |||
| 56 | # network security | ||
| 57 | /network-security.data | ||
| 58 | |||
| 59 | |||
| 60 | ### Vim ### | ||
| 61 | # Swap | ||
| 62 | [._]*.s[a-v][a-z] | ||
| 63 | [._]*.sw[a-p] | ||
| 64 | [._]s[a-rt-v][a-z] | ||
| 65 | [._]ss[a-gi-z] | ||
| 66 | [._]sw[a-p] | ||
| 67 | |||
| 68 | # Session | ||
| 69 | Session.vim | ||
| 70 | Sessionx.vim | ||
| 71 | |||
| 72 | # Temporary | ||
| 73 | .netrwhist | ||
| 74 | |||
| 75 | # Auto-generated tag files | ||
| 76 | tags | ||
| 77 | |||
| 78 | # Persistent undo | ||
| 79 | [._]*.un~ | ||
| 80 | |||
| 81 | # Coc configuration directory | ||
| 82 | .vim | ||
| 83 | |||
| 84 | ### VisualStudioCode ### | ||
| 85 | .vscode/* | ||
| 86 | !.vscode/settings.json | ||
| 87 | !.vscode/tasks.json | ||
| 88 | !.vscode/launch.json | ||
| 89 | !.vscode/extensions.json | ||
| 90 | |||
| 91 | ### VisualStudioCode Patch ### | ||
| 92 | # Ignore all local history of files | ||
| 93 | .history | ||
| 94 | |||
| 95 | # End of https://www.gitignore.io/api/vim,emacs,visualstudiocode | ||
| 96 | |||
| 97 | # -- Python -- | ||
| 98 | # Python virtual environment | ||
| 99 | .env | ||
| 100 | |||
| 101 | # Byte-compiled / optimized / DLL files | ||
| 102 | __pycache__/ | ||
| 103 | *.py[cod] | ||
| 104 | *$py.class | ||
| 105 | |||
| 106 | # C extensions | ||
| 107 | *.so | ||
| 108 | |||
| 109 | # Distribution / packaging | ||
| 110 | .Python | ||
| 111 | build/ | ||
| 112 | develop-eggs/ | ||
| 113 | dist/ | ||
| 114 | downloads/ | ||
| 115 | eggs/ | ||
| 116 | .eggs/ | ||
| 117 | lib/ | ||
| 118 | lib64/ | ||
| 119 | parts/ | ||
| 120 | sdist/ | ||
| 121 | var/ | ||
| 122 | wheels/ | ||
| 123 | pip-wheel-metadata/ | ||
| 124 | share/python-wheels/ | ||
| 125 | *.egg-info/ | ||
| 126 | .installed.cfg | ||
| 127 | *.egg | ||
| 128 | MANIFEST | ||
| 129 | |||
| 130 | # PyInstaller | ||
| 131 | # Usually these files are written by a python script from a template | ||
| 132 | # before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
| 133 | *.manifest | ||
| 134 | *.spec | ||
| 135 | |||
| 136 | # Installer logs | ||
| 137 | pip-log.txt | ||
| 138 | pip-delete-this-directory.txt | ||
| 139 | |||
| 140 | # Unit test / coverage reports | ||
| 141 | htmlcov/ | ||
| 142 | .tox/ | ||
| 143 | .nox/ | ||
| 144 | .coverage | ||
| 145 | .coverage.* | ||
| 146 | .cache | ||
| 147 | nosetests.xml | ||
| 148 | coverage.xml | ||
| 149 | *.cover | ||
| 150 | *.py,cover | ||
| 151 | .hypothesis/ | ||
| 152 | .pytest_cache/ | ||
| 153 | cover/ | ||
| 154 | |||
| 155 | # Translations | ||
| 156 | *.mo | ||
| 157 | *.pot | ||
| 158 | |||
| 159 | # Django stuff: | ||
| 160 | *.log | ||
| 161 | local_settings.py | ||
| 162 | db.sqlite3 | ||
| 163 | db.sqlite3-journal | ||
| 164 | |||
| 165 | # Flask stuff: | ||
| 166 | instance/ | ||
| 167 | .webassets-cache | ||
| 168 | |||
| 169 | # Scrapy stuff: | ||
| 170 | .scrapy | ||
| 171 | |||
| 172 | # Sphinx documentation | ||
| 173 | docs/_build/ | ||
| 174 | |||
| 175 | # PyBuilder | ||
| 176 | .pybuilder/ | ||
| 177 | target/ | ||
| 178 | |||
| 179 | # Jupyter Notebook | ||
| 180 | .ipynb_checkpoints | ||
| 181 | |||
| 182 | # IPython | ||
| 183 | profile_default/ | ||
| 184 | ipython_config.py | ||
| 185 | |||
| 186 | # pyenv | ||
| 187 | # For a library or package, you might want to ignore these files since the code is | ||
| 188 | # intended to run in multiple environments; otherwise, check them in: | ||
| 189 | # .python-version | ||
| 190 | |||
| 191 | # pipenv | ||
| 192 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||
| 193 | # However, in case of collaboration, if having platform-specific dependencies or dependencies | ||
| 194 | # having no cross-platform support, pipenv may install dependencies that don't work, or not | ||
| 195 | # install all needed dependencies. | ||
| 196 | #Pipfile.lock | ||
| 197 | |||
| 198 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow | ||
| 199 | __pypackages__/ | ||
| 200 | |||
| 201 | # Celery stuff | ||
| 202 | celerybeat-schedule | ||
| 203 | celerybeat.pid | ||
| 204 | |||
| 205 | # SageMath parsed files | ||
| 206 | *.sage.py | ||
| 207 | |||
| 208 | # Environments | ||
| 209 | .env | ||
| 210 | .venv | ||
| 211 | env/ | ||
| 212 | venv/ | ||
| 213 | ENV/ | ||
| 214 | env.bak/ | ||
| 215 | venv.bak/ | ||
| 216 | |||
| 217 | # Spyder project settings | ||
| 218 | .spyderproject | ||
| 219 | .spyproject | ||
| 220 | |||
| 221 | # Rope project settings | ||
| 222 | .ropeproject | ||
| 223 | |||
| 224 | # mkdocs documentation | ||
| 225 | /site | ||
| 226 | |||
| 227 | # mypy | ||
| 228 | .mypy_cache/ | ||
| 229 | .dmypy.json | ||
| 230 | dmypy.json | ||
| 231 | |||
| 232 | # Pyre type checker | ||
| 233 | .pyre/ | ||
| 234 | |||
| 235 | # pytype static type analyzer | ||
| 236 | .pytype/ | ||
| 237 | |||
| 238 | # Cython debug symbols | ||
| 239 | cython_debug/ | ||
| 240 | |||
| 241 | # static files generated from Django application using `collectstatic` | ||
| 242 | media | ||
| 243 | static | ||
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..334176d --- /dev/null +++ b/Cargo.toml | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | [package] | ||
| 2 | name = "python-adblock" | ||
| 3 | version = "0.1.0" | ||
| 4 | authors = ["Árni Dagur <arni@dagur.eu"] | ||
| 5 | edition = "2018" | ||
| 6 | |||
| 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
| 8 | |||
| 9 | [dependencies] | ||
| 10 | pyo3 = "0.9" | ||
| 11 | adblock = "0.2" | ||
| 12 | |||
| 13 | [lib] | ||
| 14 | name="adblock" | ||
| 15 | crate-type = ["rlib", "cdylib"] | ||
| 16 | |||
| 17 | [features] | ||
| 18 | default = ["pyo3/extension-module"] \ No newline at end of file | ||
diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..79fd056 --- /dev/null +++ b/pyproject.toml | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | [tool.poetry] | ||
| 2 | name = "adblock" | ||
| 3 | version = "0.1.0" | ||
| 4 | description = "Brave's adblocking in Python" | ||
| 5 | authors = ["Árni Dagur <arni@dagur.eu>"] | ||
| 6 | license = "Apache-2.0" | ||
| 7 | readme = "README.md" | ||
| 8 | repository = "https://github.com/ArniDagur/python-adblock" | ||
| 9 | keywords = [] | ||
| 10 | |||
| 11 | [tool.poetry.dependencies] | ||
| 12 | python = "^3.5" | ||
| 13 | |||
| 14 | [tool.poetry.dev-dependencies] | ||
| 15 | maturin = "*" | ||
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..86512f7 --- /dev/null +++ b/src/lib.rs | |||
| @@ -0,0 +1,181 @@ | |||
| 1 | //! Python bindings for Brave's adblocking library, which is written in Rust. | ||
| 2 | #![deny( | ||
| 3 | future_incompatible, | ||
| 4 | nonstandard_style, | ||
| 5 | rust_2018_idioms, | ||
| 6 | missing_copy_implementations, | ||
| 7 | trivial_casts, | ||
| 8 | trivial_numeric_casts, | ||
| 9 | unsafe_code, | ||
| 10 | unused_qualifications | ||
| 11 | )] | ||
| 12 | |||
| 13 | use pyo3::class::PyObjectProtocol; | ||
| 14 | use pyo3::prelude::*; | ||
| 15 | |||
| 16 | use adblock::blocker::BlockerResult as RustBlockerResult; | ||
| 17 | use adblock::engine::Engine as RustEngine; | ||
| 18 | |||
| 19 | /// Brave's adblocking library in Python! | ||
| 20 | #[pymodule] | ||
| 21 | fn adblock(_py: Python<'_>, m: &PyModule) -> PyResult<()> { | ||
| 22 | m.add("__version__", env!("CARGO_PKG_VERSION"))?; | ||
| 23 | m.add_class::<Engine>()?; | ||
| 24 | Ok(()) | ||
| 25 | } | ||
| 26 | |||
| 27 | #[pyclass] | ||
| 28 | pub struct BlockerResult { | ||
| 29 | #[pyo3(get)] | ||
| 30 | pub matched: bool, | ||
| 31 | #[pyo3(get)] | ||
| 32 | pub explicit_cancel: bool, | ||
| 33 | #[pyo3(get)] | ||
| 34 | pub important: bool, | ||
| 35 | #[pyo3(get)] | ||
| 36 | pub redirect: Option<String>, | ||
| 37 | #[pyo3(get)] | ||
| 38 | pub exception: Option<String>, | ||
| 39 | #[pyo3(get)] | ||
| 40 | pub filter: Option<String>, | ||
| 41 | #[pyo3(get)] | ||
| 42 | pub error: Option<String>, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl Into<BlockerResult> for RustBlockerResult { | ||
| 46 | fn into(self) -> BlockerResult { | ||
| 47 | BlockerResult { | ||
| 48 | matched: self.matched, | ||
| 49 | explicit_cancel: self.explicit_cancel, | ||
| 50 | important: self.important, | ||
| 51 | redirect: self.redirect, | ||
| 52 | exception: self.exception, | ||
| 53 | filter: self.filter, | ||
| 54 | error: self.error, | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | #[pyproto] | ||
| 60 | impl PyObjectProtocol for BlockerResult { | ||
| 61 | fn __repr__(&self) -> PyResult<String> { | ||
| 62 | Ok(format!( | ||
| 63 | "BlockerResult({}, {}, {}, {:?}, {:?}, {:?}, {:?})", | ||
| 64 | self.matched, | ||
| 65 | self.explicit_cancel, | ||
| 66 | self.important, | ||
| 67 | self.redirect, | ||
| 68 | self.exception, | ||
| 69 | self.filter, | ||
| 70 | self.error | ||
| 71 | )) | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | #[pyclass] | ||
| 76 | pub struct Engine { | ||
| 77 | engine: RustEngine, | ||
| 78 | } | ||
| 79 | |||
| 80 | #[pymethods] | ||
| 81 | impl Engine { | ||
| 82 | #[new] | ||
| 83 | pub fn from_rules(network_filters: Vec<String>) -> Self { | ||
| 84 | let engine = RustEngine::from_rules(&network_filters); | ||
| 85 | Self { engine } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// ## Request types | ||
| 89 | /// Examples of valid `request_type` parameters include: | ||
| 90 | /// * `beacon` | ||
| 91 | /// * `csp_report` | ||
| 92 | /// * `document` | ||
| 93 | /// * `font` | ||
| 94 | /// * `media` | ||
| 95 | /// * `object` | ||
| 96 | /// * `script` | ||
| 97 | /// * `stylesheet` | ||
| 98 | /// * and et cetera... | ||
| 99 | pub fn check_network_urls( | ||
| 100 | &self, | ||
| 101 | url: &str, | ||
| 102 | source_url: &str, | ||
| 103 | request_type: &str, | ||
| 104 | ) -> BlockerResult { | ||
| 105 | let blocker_result = self | ||
| 106 | .engine | ||
| 107 | .check_network_urls(url, source_url, request_type); | ||
| 108 | blocker_result.into() | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn check_network_urls_with_hostnames( | ||
| 112 | &self, | ||
| 113 | url: &str, | ||
| 114 | hostname: &str, | ||
| 115 | source_hostname: &str, | ||
| 116 | request_type: &str, | ||
| 117 | third_party_request: Option<bool>, | ||
| 118 | ) -> BlockerResult { | ||
| 119 | let blocker_result = self.engine.check_network_urls_with_hostnames( | ||
| 120 | url, | ||
| 121 | hostname, | ||
| 122 | source_hostname, | ||
| 123 | request_type, | ||
| 124 | third_party_request, | ||
| 125 | ); | ||
| 126 | blocker_result.into() | ||
| 127 | } | ||
| 128 | |||
| 129 | pub fn check_network_urls_with_hostnames_subset( | ||
| 130 | &self, | ||
| 131 | url: &str, | ||
| 132 | hostname: &str, | ||
| 133 | source_hostname: &str, | ||
| 134 | request_type: &str, | ||
| 135 | third_party_request: Option<bool>, | ||
| 136 | previously_matched_rule: bool, | ||
| 137 | force_check_exceptions: bool, | ||
| 138 | ) -> BlockerResult { | ||
| 139 | let blocker_result = self.engine.check_network_urls_with_hostnames_subset( | ||
| 140 | url, | ||
| 141 | hostname, | ||
| 142 | source_hostname, | ||
| 143 | request_type, | ||
| 144 | third_party_request, | ||
| 145 | previously_matched_rule, | ||
| 146 | force_check_exceptions, | ||
| 147 | ); | ||
| 148 | blocker_result.into() | ||
| 149 | } | ||
| 150 | |||
| 151 | pub fn filter_exists(&self, filter: &str) -> bool { | ||
| 152 | self.engine.filter_exists(filter) | ||
| 153 | } | ||
| 154 | |||
| 155 | pub fn tags_enable(&mut self, tags: Vec<&str>) { | ||
| 156 | self.engine.tags_enable(&tags); | ||
| 157 | } | ||
| 158 | |||
| 159 | pub fn tags_disable(&mut self, tags: Vec<&str>) { | ||
| 160 | self.engine.tags_disable(&tags); | ||
| 161 | } | ||
| 162 | |||
| 163 | pub fn tag_exists(&self, tag: &str) -> bool { | ||
| 164 | self.engine.tag_exists(tag) | ||
| 165 | } | ||
| 166 | |||
| 167 | // pub fn hidden_class_id_selectors( | ||
| 168 | // &self, | ||
| 169 | // classes: Vec<String>, | ||
| 170 | // ids: Vec<String>, | ||
| 171 | // exceptions: &PySet, | ||
| 172 | // ) -> PyResult<Vec<String>> { | ||
| 173 | // let mut exception_hashset: HashSet<String> = HashSet::new(); | ||
| 174 | // for exception in exceptions.iter() { | ||
| 175 | // let exception_pystr = PyString::from(exception); | ||
| 176 | // exception_hashset.insert(exception.to_string()); | ||
| 177 | // } | ||
| 178 | // Ok(self.engine | ||
| 179 | // .hidden_class_id_selectors(&classes, &ids, &exception_hashset)) | ||
| 180 | // } | ||
| 181 | } | ||
