{
"info": {
"author": "Antonio Cavedoni",
"author_email": "antonio@cavedoni.org",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 3 - Alpha",
"Environment :: MacOS X",
"Environment :: MacOS X :: Cocoa",
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS :: MacOS X",
"Programming Language :: Python :: 3",
"Topic :: Internet"
],
"description": "[
](https://github.com/verbosus/urlreader/actions)\n\n# URLReader\n\nURLReader is a wrapper around macOS\u2019s NSURLSession, etc. \n\n## Scope & Limitations\n\nURLReader originated from [an effort](https://github.com/robofont-mechanic/mechanic-2/pull/18) to improve the UI responsiveness of [Mechanic](https://robofontmechanic.com/), a package manager for [RoboFont](https://www.robofont.com/). Because of this original use-case, URLReader is meant to be used in PyObjC apps or scripts that need to download and possibly cache relatively small bits of additional data. Technically there is nothing preventing you from using URLReader for other use-cases, larger downloads, etc. but depending what these might be you might be better served by using `NSURLSession` directly.\n\nBeing powered by the system `NSURLSession`, URLReader only works on macOS 10.9+.\n\n## Basic usage\n\nIn its most basic form, URLReader takes a URL, fetches its contents in the background (so it won\u2019t block your UI) and sends them back on the main thread to a callback you provide. The callback is a regular Python function with three arguments: `url` , `data` and `error` (of types `NSURL`, `NSData` and `NSError`). Like this:\n\n```python\ndef callback(url, data, error):\n if url and data and not error:\n print(f\"Received {url} contents, {len(data)} bytes\")\n\nURLReader().fetch(\"http://example.org/\", callback)\n```\n\n## Timeout\n\nYou can set a custom timeout for requests (which by default is 10 seconds):\n\n```python\nURLReader(timeout=2) # in seconds\n```\n\nNotice that this is the total response time, not how long it takes for the initial request to make it to the server. \n\n## Quote URL path and force HTTPS\n\nSometimes people have spaces in their URL paths, like, say, `/Foo Bar`, but forget to quote them. The `NSURLSession` reading machinery really doesn\u2019t like that. By default URLReader quotes the path component of a URL. This behavior can be turned off, if needed:\n\n```python\nURLReader(quote_url_path=False)\n```\n\nAlso, sometimes people try to open `http` URLs, but their apps\u2019 App Security Policy won\u2019t allow that. If the remote server also supports `https`, then you can tell URLReader to promote the URLs from `http` to `https` by calling:\n\n```python\nURLReader(force_https=True)\n```\n\nThis setting won\u2019t affect non-HTTP requests. Technically there is nothing in URL-reader that is HTTP-only, and `NSURLSession` does support other protocols. I haven\u2019t tested it with these, but I\u2019m not aware of anything special that would need to change. \n\n## Usage from scripts\n\nYou can use URLReader in synchronous mode for one-off scripts if you need to. It will block until the response is fully returned to your callback instead of calling it asynchronously like it normally would:\n\n```python\nURLReader(wait_until_done=True)\n```\n\n## Caching\n\nBy default, URLReader follows the caching policy set by the protocol, i.e. [NSURLRequestUseProtocolCachePolicy](https://developer.apple.com/documentation/foundation/nsurlrequestcachepolicy/nsurlrequestuseprotocolcachepolicy) which means it will do whatever the response HTTP caching headers tell it to do (if the request is HTTP.)\n\nIn some cases, however, you might want to cache the response so it\u2019s available to your code even when offline. In these situations, URLReader switches the request cache policy to [NSURLRequestReturnCacheDataElseLoad](https://developer.apple.com/documentation/foundation/nsurlrequestcachepolicy/nsurlrequestreturncachedataelseload) which means it will first check its cache and return data from there no matter what the HTTP headers say. \n\nOn top of the standard `NSURLRequestReturnCacheDataElseLoad` behavior, URLReader adds a little twist: if the original response required one or more redirects, which wouldn\u2019t be cached therefore not available offline, NSURLCache isn\u2019t able to fetch the response from its cache. URLReader adds a cache entry for the response URL *before the redirects* so they are fully accessible offline.\n\nThis all happens with the same code: \n\n```python \nURLReader(use_cache=True) # this switches on the caching mechanism described above\n``` \n\nBy default the cache will be written in `~/Library/Caches/` but that can be configured like this:\n\n```python\nURLReader(use_cache=True, cache_location=\"/my/cache/path\") # cache_location can be either a string path or an NSURL\n```\n\nOnce a URL is fetched by an URLReader with caching enabled, it stays in the cache indefinitely. You can force-reset a cached entry by setting `invalidate_cache=True` on the `fetch` method:\n\n```python\nreader.fetch(url, callback, invalidate_cache=True)\n```\n\nOr you can remove the cached entry without replacing it with a new one:\n\n```python\nreader.invalidate_cache_for_url(url)\n```\n\nFlushing the cache, removing all the cached entries, is also possible:\n\n```python\nreader.flush_cache()\n```\n\n## Callback\n\nSometimes you just need to fetch some values, quick. You could use a lambda:\n\n```python\nURLReader().fetch('http://example.org',\n lambda url, data, error: print(f'Received {len(data)} bytes'))\n```\n\nJust remember the lambda would be called asynchronously and the call would return right away. So it\u2019d be no good in a script without an NSRunLoop. In that case, this would work:\n\n```python\nURLReader(wait_until_done=True).fetch('http://example.org',\n lambda url, data, error: print(f'Received {len(data)} bytes'))\n```\n\nThis would make the call block until the data is received and the lambda has exited. But then again, if you just want to fetch a URL in a blocking fashion with no caches or other accoutrements, either `requests.get(url)` or `urllib.request.urlopen(url)` are probably a better fit.\n\n## Tests\n\nURLReader has a small test suite which spins a simple HTTP server in a separate process and runs against it. You can run it with the `tests.py` script in the main directory. The test suite is also [hooked up to Github\u2019s actions](https://github.com/verbosus/urlreader/actions), so it runs whenever there\u2019s a push on the repo.\n\n## And that\u2019s it\n\nThanks for reading, hope this code is useful somehow.\n\n---\n\nURLReader was written by [Antonio Cavedoni](mailto:antonio@cavedoni.org) and is licensed under the terms of the [MIT License](https://github.com/verbosus/urlreader/blob/master/LICENSE).\n\n\n",
"description_content_type": "text/markdown",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/verbosus/urlreader",
"keywords": "",
"license": "",
"maintainer": "",
"maintainer_email": "",
"name": "urlreader",
"package_url": "https://pypi.org/project/urlreader/",
"platform": "",
"project_url": "https://pypi.org/project/urlreader/",
"project_urls": {
"Homepage": "https://github.com/verbosus/urlreader"
},
"release_url": "https://pypi.org/project/urlreader/0.1.2/",
"requires_dist": [
"pyobjc (>=5.2)"
],
"requires_python": ">=3.6",
"summary": "URLReader: a wrapper around macOS\u2019s NSURLSession, etc. for PyObjC apps",
"version": "0.1.2"
},
"last_serial": 5964500,
"releases": {
"0.1.2": [
{
"comment_text": "",
"digests": {
"md5": "6c000caf377c5b04f331e4844f131c8e",
"sha256": "60b1da6e0bb08a91ef7335d56853f23f60bc10a65eb18139a6b3c84edce2bb52"
},
"downloads": -1,
"filename": "urlreader-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6c000caf377c5b04f331e4844f131c8e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 7982,
"upload_time": "2019-10-12T14:36:29",
"url": "https://files.pythonhosted.org/packages/1d/bd/f6f6004b186555646c71f91ad17e9c9e501b13d0cd73e39e80fb17d64455/urlreader-0.1.2-py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "b336783d1172821ed7319f4cbd0eaa6a",
"sha256": "a570775c7a5e523024e4d61b76b9e19b6ae273c277693430df2fb108aeb71b63"
},
"downloads": -1,
"filename": "urlreader-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "b336783d1172821ed7319f4cbd0eaa6a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 6822,
"upload_time": "2019-10-12T14:36:31",
"url": "https://files.pythonhosted.org/packages/86/4b/857788f17971662557bfcf156d38c594daef746627c9f874924acb780950/urlreader-0.1.2.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "6c000caf377c5b04f331e4844f131c8e",
"sha256": "60b1da6e0bb08a91ef7335d56853f23f60bc10a65eb18139a6b3c84edce2bb52"
},
"downloads": -1,
"filename": "urlreader-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6c000caf377c5b04f331e4844f131c8e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 7982,
"upload_time": "2019-10-12T14:36:29",
"url": "https://files.pythonhosted.org/packages/1d/bd/f6f6004b186555646c71f91ad17e9c9e501b13d0cd73e39e80fb17d64455/urlreader-0.1.2-py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "b336783d1172821ed7319f4cbd0eaa6a",
"sha256": "a570775c7a5e523024e4d61b76b9e19b6ae273c277693430df2fb108aeb71b63"
},
"downloads": -1,
"filename": "urlreader-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "b336783d1172821ed7319f4cbd0eaa6a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 6822,
"upload_time": "2019-10-12T14:36:31",
"url": "https://files.pythonhosted.org/packages/86/4b/857788f17971662557bfcf156d38c594daef746627c9f874924acb780950/urlreader-0.1.2.tar.gz"
}
]
}