Commit 65ce83c4 authored by Martmists's avatar Martmists

Merge branch 'memory-upload' into 'master'

Allow uploading of `bytes` or file-like objects from memory

Closes #10

See merge request !10
parents d2904ab7 ac23c2d3
......@@ -11,6 +11,9 @@ import owo
# Asynchronous Only Imports
import asyncio
# Optional Import for Making a Byte Stream
from io import BytesIO
# ############################################################
key = input("Please enter your API key: ")
......@@ -28,13 +31,34 @@ print(res)
loop = asyncio.get_event_loop()
# Upload Image
res = loop.run_until_complete(owo.async_upload_files(key,
"test.png",
loop=loop))
res = loop.run_until_complete(
owo.async_upload_files(key, "example.png", loop=loop))
print(res)
# Shorten URL
res = loop.run_until_complete(owo.async_shorten_urls(key,
"http://google.com",
loop=loop))
res = loop.run_until_complete(
owo.async_shorten_urls(key, "http://google.com", loop=loop))
print(res)
# ############################################################
# Upload Image from Memory
f = open("./example.png", "rb")
res = owo.upload_files(key, f)
res2 = owo.upload_files(key, BytesIO(b"this is my very cool file"))
print(res)
print(res2)
# Upload Image from Memory (Async)
loop = asyncio.get_event_loop()
f = open("./example.png", "rb")
res = loop.run_until_complete(owo.async_upload_files(key, f))
res2 = loop.run_until_complete(
owo.async_upload_files(key, BytesIO(b"this is my very cool file")))
print(res)
print(res2)
This is a cool example text file. Obviously much more cooler than anything else you've ever seen.
You wish you were this cool.
\ No newline at end of file
......@@ -10,78 +10,62 @@
# If you don't want to pass your key each time,
# define it within the Client class.
>>> import owo
>>> my_client = owo.Client(API_KEY)
import owo
API_KEY = input("Please enter your API key: ")
url1 = "https://google.com"
url2 = "https://whats-th.is"
my_client = owo.Client(API_KEY)
# Then the following methods can be changed to..
>>> my_client.shorten_urls("url1", "url2")
my_client.shorten_urls(url1, url2)
# The client also stores verbosity (default: False), which can be toggled using
# Client.toggle_verbose()
# Verbosity should be given using a kwarg to __init__, i.e.
>>> my_client = owo.Client(API_KEY, verbose=True)
>>> my_client.toggle_verbose()
>>> my_client.verbose
False
my_client = owo.Client(API_KEY, verbose=True)
my_client.toggle_verbose()
my_client.verbose # False
# ############################################################
# NON ASYNCHRONOUS EXAMPLES
# ############# #
# EXAMPLE INPUT #
# ############# #
# Import the wrapper in order to be used.
>>> import owo
# Specify which files **IN THE SAME FOLDER** that you would like to upload.
>>> owo.shorten_urls(API_KEY, "url1", "url2")
# ############## #
# EXAMPLE OUTPUT #
# ############## #
# Specify which urls you want to shorten.
owo.shorten_urls(API_KEY, url1, url2)
["shortened url 1", "shortened url 2"]
# Example output: ["shortened url 1", "shortened url 2"]
# It is also possible to toggle verbosity
>>> owo.shorten_urls(API_KEY, "url1", "url2", verbose=True)
# ############## #
# EXAMPLE OUTPUT #
# ############## #
[
{
'base domain 1': 'shortened url 1',
'base domain 2': 'other shortened url 1'
},
{
'base domain 1': 'shortened url 2',
'base domain 2': 'other shortened url 2'
}
]
owo.shorten_urls(API_KEY, url1, url2, verbose=True)
# Example output:
# [
# {
# 'base domain 1': 'shortened url 1',
# 'base domain 2': 'other shortened url 1'
# },
# {
# 'base domain 1': 'shortened url 2',
# 'base domain 2': 'other shortened url 2'
# }
# ]
# ############################################################
# ASYNCHRONOUS EXAMPLES
# ############# #
# EXAMPLE INPUT #
# ############# #
import asyncio
>>> import asyncio
>>> loop = asyncio.get_event_loop()
>>> my_client = owo.Client(API_KEY, loop=loop)
>>> loop.run_until_complete(
... my_client.async_shorten_urls("url1", "url2"))
...
loop = asyncio.get_event_loop()
my_client = owo.Client(API_KEY, loop=loop)
# ############## #
# EXAMPLE OUTPUT #
# ############## #
loop.run_until_complete(
my_client.async_shorten_urls(url1, url2))
["shortened url 1", "shortened url 2"]
# Example output: ["shortened url 1", "shortened url 2"]
# ############################################################
......@@ -10,69 +10,81 @@
# If you don't want to pass your key each time,
# define it within the Client class.
>>> import owo
>>> my_client = owo.Client(API_KEY)
import owo
API_KEY = input("Please enter your API key: ")
my_client = owo.Client(API_KEY)
# Then the following methods can be changed to..
>>> my_client.upload_files("file.png", "file.py")
my_client.upload_files("example.png", "example.txt")
# The client also stores verbosity (default: False), which can be toggled using
# Client.toggle_verbose()
# Verbosity should be given using a kwarg to __init__, i.e.
>>> my_client = owo.Client(API_KEY, verbose=True)
>>> my_client.toggle_verbose()
>>> my_client.verbose
False
my_client = owo.Client(API_KEY, verbose=True)
my_client.toggle_verbose()
my_client.verbose # False
# ############################################################
# NON ASYNCHRONOUS EXAMPLES
# ############# #
# EXAMPLE INPUT #
# ############# #
# Import the wrapper in order to be used.
>>> import owo
# Specify which files **IN THE SAME FOLDER** that you would like to upload.
>>> owo.upload_files(API_KEY, "file.png", "file.py")
# ############## #
# EXAMPLE OUTPUT #
# ############## #
# Specify which files that you would like to upload.
owo.upload_files(API_KEY, "example.png", "example.txt")
{"file.png": "url", "file.py": "url"}
# Example output: {"example.png": "url", "example.txt": "url"}
# It is also possible to toggle verbosity
>>> owo.upload_files(API_KEY, "file.png", "file.py", verbose=True)
owo.upload_files(API_KEY, "example.png", "example.txt", verbose=True)
# ############## #
# EXAMPLE OUTPUT #
# ############## #
{
'file.png': {'base domain 1': 'url 1', 'base domain 2': 'other url 1'},
'file.py': {'base domain 1': 'url 2', 'base domain 2': 'other url 2'}
}
# Example output:
# {
# 'example.png': {'base domain 1': 'url 1', 'base domain 2': 'other url 1'},
# 'example.txt': {'base domain 1': 'url 2', 'base domain 2': 'other url 2'}
# }
# ############################################################
# ASYNCHRONOUS EXAMPLES
# ############# #
# EXAMPLE INPUT #
# ############# #
import asyncio
>>> loop.run_until_complete(
... owo.async_upload_files(API_KEY, "file.png", "file.py", loop=loop))
...
loop = asyncio.get_event_loop()
# ############## #
# EXAMPLE OUTPUT #
# ############## #
loop.run_until_complete(
owo.async_upload_files(API_KEY, "example.png", "example.txt", loop=loop))
{"file.png": "url", "file.py": "url"}
# Example output: {"example.png": "url", "example.txt": "url"}
# ############################################################
# IN-MEMORY EXAMPLES
# You can also pass `upload_files` and `async_upload_files` a bytes object or
# an object that inherits from `io.IOBase`, which includes objects like files
# from `open()`, or `BytesIO`.
import io
owo.upload_files(API_KEY, open("./example.png", "rb"))
# Example output: {"example.png": "url"}
loop.run_until_complete(
owo.async_upload_files(API_KEY, io.BytesIO(b"My very cool file"), loop=loop
))
# Example output: {"file_0": "url"}
# If the object has a `name` property, that will be used as the filename
# passed when uploading, otherwiose it'll take its index and make its filename
# `file_i`, where 'i' is the index.
# Multiple unnamed objects
owo.upload_files(API_KEY, io.BytesIO(b"File one"), io.BytesIO(b"File two"))
# Example output: {"file_0": "url", "file_1": "url"}
\ No newline at end of file
import asyncio
import io
import mimetypes
import os.path as osp
from .utils import check_size, BASE_URL, MAX_FILES,\
......@@ -10,6 +12,7 @@ from .utils import check_size, BASE_URL, MAX_FILES,\
def async_upload_files(key, *files, **kwargs):
verbose = kwargs.get("verbose", False)
loop = kwargs.get("loop", None)
if len(files) > MAX_FILES:
raise OverflowError("Maximum amout of files to send at once"
"is {}".format(MAX_FILES))
......@@ -23,16 +26,37 @@ def async_upload_files(key, *files, **kwargs):
results = {}
for file in files:
if not isinstance(file, (str, bytes, io.IOBase)):
raise ValueError("`file` should either be a `str`, `bytes` or an"
"inheriter of `io.IOBase` (open(), BytesIO,"
"etc.).")
check_size(file)
with aiohttp.MultipartWriter('form-data') as mp:
for file in files:
part = mp.append(open(file, "rb"))
for i, file in enumerate(files):
if isinstance(file, str):
# If string, read file
data = open(file, "rb")
name = file
else:
data = file
name = getattr(file, "name", "file_{}".format(i))
# Get only the filename, with no path.
# Without this, attempting to upload files like `./foo.ext`
# (with any path stuff, `./`, `dir/`, etc) will result in an
# annoying error saying that there were no `files[]`.
name = osp.basename(name).lower()
part = mp.append(data, {'Content-Type':
mimetypes.guess_type(name)[0] or
'application/octet-stream'})
part.set_content_disposition(
'form-data',
quote_fields=False,
name='files[]',
filename=osp.basename(file).lower() # Errors without basename
filename=name
)
session = aiohttp.ClientSession(loop=loop)
......@@ -64,6 +88,7 @@ def async_upload_files(key, *files, **kwargs):
def async_shorten_urls(key, *urls, **kwargs):
verbose = kwargs.get("verbose", False)
loop = kwargs.get("loop", None)
try:
import aiohttp
except ImportError:
......
import io
import mimetypes
import os.path as osp
import sys
try:
......@@ -34,6 +36,7 @@ else:
@lru_cache(maxsize=None)
def upload_files(key, *files, **kwargs):
verbose = kwargs.get("verbose", False)
if len(files) > MAX_FILES:
raise OverflowError("Maximum amout of files to send at once"
"is {}".format(MAX_FILES))
......@@ -45,13 +48,28 @@ def upload_files(key, *files, **kwargs):
"to use this function")
for file in files:
if not isinstance(file, (str, bytes, io.IOBase)):
raise ValueError("`file` should either be a `str`, `bytes` or an"
"inheriter of `io.IOBase` (open(), BytesIO,"
"etc.).")
check_size(file)
multipart = [(
"files[]",
(file.lower(), open(file, "rb"),
mimetypes.guess_type(file)[0])
) for file in files]
multipart = []
for i, file in enumerate(files):
if isinstance(file, str):
# Get only the filename, with no path.
name = osp.basename(file).lower()
multipart.append(("files[]",
(name, open(file, "rb"),
mimetypes.guess_type(file)[0])))
else:
name = getattr(file, "name", "file_{}".format(i))
name = osp.basename(name).lower()
multipart.append(("files[]",
(name, file,
mimetypes.guess_type(name)[0])))
response = requests.post(BASE_URL+UPLOAD_PATH, files=multipart,
params={"key": key},
......@@ -82,6 +100,7 @@ def upload_files(key, *files, **kwargs):
@lru_cache(maxsize=None)
def shorten_urls(key, *urls, **kwargs):
verbose = kwargs.get("verbose", False)
try:
import requests
except ImportError:
......
import os
import os.path
import re
import requests
DOMAINS_URL = ("https://raw.githubusercontent.com/whats-this/landing/"
......@@ -12,9 +13,10 @@ version = "2.3.0"
headers = {
"User-Agent": ("WhatsThisClient (https://github.com/whats-this/owo.py,"
f" {version}),")}
" {}),".format(version))}
MAX_FILES = 3
MAX_SIZE = 83889080
BASE_URL = "https://api.awau.moe"
......@@ -24,7 +26,7 @@ SHORTEN_PATH = "/shorten/polr"
UPLOAD_STANDARD = "https://owo.whats-th.is/"
SHORTEN_STANDARD = "https://uwu.whats-th.is/"
UPLOAD_BASES = ["https://{}/".format(url.split(":")[-1]) for url in
UPLOAD_BASES = ["https://{}/".format(url.split(":")[-1]) for url in
content.split("\n")
if "#" not in url]
......@@ -32,5 +34,20 @@ SHORTEN_BASES = UPLOAD_BASES
def check_size(file):
if os.path.getsize(file) > 83886080:
if isinstance(file, str):
check = os.path.getsize(file) > MAX_SIZE
elif isinstance(file, bytes):
check = len(file) > MAX_SIZE
else:
# Should work for just about any file-like object (`open`, BytesIO, etc)
# without consuming it.
old_pos = file.tell()
file.seek(0, os.SEEK_END)
size = file.tell()
file.seek(old_pos, os.SEEK_SET)
check = size > MAX_SIZE
if check:
raise OverflowError("File exceeds file size limit")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment