GithubHelp home page GithubHelp logo

uploadcare / pyuploadcare Goto Github PK

View Code? Open in Web Editor NEW
123.0 15.0 32.0 3.31 MB

Build file handling in minutes. Upload or accept user-generated content, store, transform, optimize, and deliver images, videos, and documents to billions of users.

Home Page: https://uploadcare.com/

License: MIT License

Python 99.02% Makefile 0.57% HTML 0.41%
python upload uploader cdn image-processing image-manipulation file-upload file-system

pyuploadcare's Introduction

Uploadcare logo

Package DocsUploadcare DocsUpload API ReferenceREST API ReferenceURL API ReferenceWebsite

Python API client for Uploadcare

Build file handling in minutes. Upload or accept user-generated content, store, transform, optimize, and deliver images, videos, and documents to billions of users.

Description

This library consists of the APIs interface and a couple of Django goodies, 100% covering Upload, REST and URL Uploadcare APIs.

  • Upload files from anywhere via API or ready-made File Uploader
  • Manage stored files and perform various actions and conversions with them
  • Optimize and transform images on the fly
  • Deliver files fast and secure

Documentation

Detailed specification of this library is available on RTD.

Please note that this package uses Uploadcare API keys and is intended to be used in server-side code only.

Installation

In order to install pyuploadcare, run these command in CLI:

pip install pyuploadcare

To use in Django project install with extra dependencies:

pip install pyuploadcare[django]

Requirements

  • Python 3.8, 3.9, 3.10, 3.11, 3.12

To use pyuploadcare with Python 3.6 or 3.7 please install pyuploadcare < 5.0.

To use pyuploadcare with Python 2.7 please install pyuploadcare < 3.0.

Django compatibility:

Py/Dj 2.2 3.0 3.1 3.2 4.0 4.1 4.2 5.0
3.8 X X X X X X X
3.9 X X X X X X X
3.10 X X X X X
3.11 X X X
3.12 X X

Usage

After package installation, you’ll need API keys: public and secret. Get them in Uploadcare dashboard. If you don’t have an account yet, you can use demo keys, as in example. However, the files on demo account are regularly removed, so create an account as soon as Uploadcare catches your fancy.

In these examples we’re going to use the aforementioned demo keys.

Basic usage

Let’s start with the basics. Say, you want to upload a file:

from pyuploadcare import Uploadcare

uploadcare = Uploadcare(public_key="demopublickey", secret_key="demoprivatekey")
with open("sample-file.jpeg", "rb") as file_object:
    ucare_file = uploadcare.upload(file_object)

And your file is now uploaded to the Uploadcare CDN. But how do you access it from the web? It’s really simple:

print(ucare_file.cdn_url)  # file URL, e.g.: https://ucarecdn.com/640fe4b7-7352-42ca-8d87-0e4387957157/

And what about information about the file?

from pprint import pprint

pprint(ucare_file.info)

# {'appdata': None,
#  'content_info': {'image': {'color_mode': <ColorMode.RGB: 'RGB'>,
#                             'datetime_original': datetime.datetime(2023, 3, 10, 16, 23, 15),
#                             'dpi': (72, 72),
#                             'format': 'JPEG',
#                             'geo_location': None,
#                             'height': 4516,
#                             'orientation': 1,
#                             'sequence': False,
#                             'width': 3011},
#                   'mime': {'mime': 'image/jpeg',
#                            'subtype': 'jpeg',
#                            'type': 'image'},
#                   'video': None},
#  'datetime_removed': None,
#  'datetime_stored': datetime.datetime(2024, 2, 16, 14, 44, 29, 637342, tzinfo=TzInfo(UTC)),
#  'datetime_uploaded': datetime.datetime(2024, 2, 16, 14, 44, 29, 395043, tzinfo=TzInfo(UTC)),
#  'is_image': True,
#  'is_ready': True,
#  'metadata': {},
#  'mime_type': 'image/jpeg',
#  'original_file_url': 'https://ucarecdn.com/640fe4b7-7352-42ca-8d87-0e4387957157/samplefile.jpeg',
#  'original_filename': 'sample-file.jpeg',
#  'size': 3518420,
#  'source': None,
#  'url': 'https://api.uploadcare.com/files/640fe4b7-7352-42ca-8d87-0e4387957157/',
#  'uuid': UUID('640fe4b7-7352-42ca-8d87-0e4387957157'),
#  'variations': None}

A whole slew of different file operations are available. Do you want to crop your image, but don't want important information (faces, objects) to be cropped? You can do that with content-aware (“smart”) crop:

from pyuploadcare.transformations.image import ImageTransformation, ScaleCropMode

# These two function calls are equivalent
ucare_file.set_effects("scale_crop/512x512/smart/")
ucare_file.set_effects(ImageTransformation().scale_crop(512, 512, mode=ScaleCropMode.smart))

print(ucare_file.cdn_url)  # https://ucarecdn.com/640fe4b7-7352-42ca-8d87-0e4387957157/-/scale_crop/512x512/smart/

There’s a lot more to uncover. For more information please refer to the documentation.

Django integration

Let's add File Uploader to an existing Django project.

We will allow users to upload their images through a nice and modern UI within the standard Django admin or outside of it, and then display these images on the website and modify them using advanced Uploadcare CDN features.

Assume you have a Django project with gallery app.

Add pyuploadcare.dj into INSTALLED_APPS:

INSTALLED_APPS = (
    # ...
    "pyuploadcare.dj",
    "gallery",
)

Add API keys to your Django settings file:

UPLOADCARE = {
    "pub_key": "demopublickey",
    "secret": "demoprivatekey",
}

Uploadcare image field adding to your gallery/models.py is really simple. Like that:

from django.db import models

from pyuploadcare.dj.models import ImageField


class Photo(models.Model):
    title = models.CharField(max_length=255)
    photo = ImageField()

ImageField doesn’t require any arguments, file paths or whatever. It just works. That’s the point of it all. It looks nice in the admin interface as well:

Obviously, you would want to use Uploadcare field outside an admin. It’s going to work just as well, but, however, you have to remember to add {{ form.media }} in the <head> tag of your page:

{{ form.media }}

<form action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Save"/>
</form>

This is a default Django form property which is going to render any scripts needed for the form to work, in our case — Uploadcare scripts.

After an image is uploaded, you can deliver it while transforming it on the fly:

{% for photo in photos %}
    <h2>{{ photo.title }}</h2>
    <img src="{{ photo.photo.cdn_url }}-/resize/400x300/-/effect/flip/-/effect/grayscale/">
{% endfor %}

(Refer to Uploadcare image processing docs for more information).

Testing

To run tests using Github Actions workflows, but locally, install the act utility, and then run it:

make test_with_github_actions

This runs the full suite of tests across Python and Django versions.

Demo app

We've developed a demo app that showcases most of the features. You can install pyuploadcare-example using Docker or without it. You can use it as a reference or even base your project on it.

Suggestions and questions

Contributing guide
Security policy
Support

pyuploadcare's People

Contributors

andrewshkovskii avatar art-vasilyev avatar caxap avatar dayton1987 avatar dimier avatar dmitry-mukhin avatar evgkirov avatar homm avatar ivanredbread avatar ivlevdenis avatar khazamov avatar marselester avatar mikhail-iurkov avatar rsedykh avatar sharov avatar slafs avatar valyagolev avatar wreckah avatar yumitsu avatar zerc avatar zmoki avatar zowie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyuploadcare's Issues

Error in Django Admin - render() got an unexpected keyword argument 'renderer'

Using ImageField in a model and viewing a django model object with it.

Full stack trace

Environment:


Request Method: GET
Request URL: http://localhost:8000/admin/partner/partner/1/change/

Django Version: 2.1.2
Python Version: 3.7.0
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'crispy_forms',
 'jsoneditor',
 'pyuploadcare.dj',
 'core',
 'partner']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Template error:
In template PATH/lib/python3.7/site-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 19
   render() got an unexpected keyword argument 'renderer'
   9 :             {% for field in line %}
   10 :                 <div{% if not line.fields|length_is:'1' %} class="fieldBox{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}"{% elif field.is_checkbox %} class="checkbox-row"{% endif %}>
   11 :                     {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %}
   12 :                     {% if field.is_checkbox %}
   13 :                         {{ field.field }}{{ field.label_tag }}
   14 :                     {% else %}
   15 :                         {{ field.label_tag }}
   16 :                         {% if field.is_readonly %}
   17 :                             <div class="readonly">{{ field.contents }}</div>
   18 :                         {% else %}
   19 :                              {{ field.field }} 
   20 :                         {% endif %}
   21 :                     {% endif %}
   22 :                     {% if field.field.help_text %}
   23 :                         <div class="help">{{ field.field.help_text|safe }}</div>
   24 :                     {% endif %}
   25 :                 </div>
   26 :             {% endfor %}
   27 :         </div>
   28 :     {% endfor %}
   29 : </fieldset>


Traceback:

File "PATH/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "PATH/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  156.                 response = self.process_exception_by_middleware(e, request)

File "PATH/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  154.                 response = response.render()

File "PATH/lib/python3.7/site-packages/django/template/response.py" in render
  106.             self.content = self.rendered_content

File "PATH/lib/python3.7/site-packages/django/template/response.py" in rendered_content
  83.         content = template.render(context, self._request)

File "PATH/lib/python3.7/site-packages/django/template/backends/django.py" in render
  61.             return self.template.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  171.                     return self._render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in _render
  163.         return self.nodelist.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/loader_tags.py" in render
  150.             return compiled_parent._render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in _render
  163.         return self.nodelist.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/loader_tags.py" in render
  150.             return compiled_parent._render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in _render
  163.         return self.nodelist.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/loader_tags.py" in render
  62.                 result = block.nodelist.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/loader_tags.py" in render
  62.                 result = block.nodelist.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/defaulttags.py" in render
  209.                     nodelist.append(node.render_annotated(context))

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/loader_tags.py" in render
  188.             return template.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  173.                 return self._render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in _render
  163.         return self.nodelist.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/defaulttags.py" in render
  209.                     nodelist.append(node.render_annotated(context))

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/defaulttags.py" in render
  209.                     nodelist.append(node.render_annotated(context))

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/defaulttags.py" in render
  309.                 return nodelist.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/defaulttags.py" in render
  309.                 return nodelist.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  937.                 bit = node.render_annotated(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_annotated
  904.             return self.render(context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render
  993.         return render_value_in_context(output, context)

File "PATH/lib/python3.7/site-packages/django/template/base.py" in render_value_in_context
  972.             value = str(value)

File "PATH/lib/python3.7/site-packages/django/utils/html.py" in <lambda>
  397.     klass.__str__ = lambda self: mark_safe(klass_str(self))

File "PATH/lib/python3.7/site-packages/django/forms/boundfield.py" in __str__
  33.         return self.as_widget()

File "PATH/lib/python3.7/site-packages/django/forms/boundfield.py" in as_widget
  93.             renderer=self.form.renderer,

Exception Type: TypeError at /admin/partner/partner/1/change/
Exception Value: render() got an unexpected keyword argument 'renderer'

can't save a model

Hi there!

Recently i've upgrade from 0.5 to 0.7. And can't get saving a model to work. Here's a traceback
http://dpaste.com/809051/.

Haven't been tracking the changes lately but would like to catch up.
Can you guide me what could be wrong?

I've debuged that the 404 response is when doing PUT on http://api.uploadcare.com/files/ff5d2597-8d97-4d92-8d03-135f3382c74c/storage/ with those headers {'Content-Length': '0', 'Accept': 'application/vnd.uploadcare-v0.2+json', 'User-Agent': 'pyuploadcare/0.7', 'Authentication': 'UploadCare ****************:*****', 'Date': 'Tue, 02 Oct 2012 18:10:51 GMT', 'Content-Type': 'application/json'}

Update httpx dependency

We have to bump httpx dependency.
They have breaking changes in minor version, so please be careful.
One thing I found is we have to rename allow_redirects to follow_redirects

AkamaiSecureUrlBuilder creates not working urls for files

Describe the bug

AkamaiSecureUrlBuilder claims to follow secure delivery guidelines described in https://uploadcare.com/docs/security/secure-delivery/ but the urls generated by it don't work (HTTP 403).

Reference implementation uses Akamai EdgeAuth that differs a bit from what is implemented in this project:

  • Akamai implementation uses sha256 hash function while pypuloadcare uses sha1 by default. This isn't an issue since it can be overwritten.
  • Akamai doesn't use acl in hashed values, nor in the token itself.

Code / screenshots

Uploadcare(..., secure_url_builder=AkamaiSecureUrlBuilder(...)).generate_secure_url("<uuid>") results in https://mydomain.com/<uuid>/?token=exp=...~acl=/<uuid>/~hmac=...

while

EdgeAuth(...).generate_url_token("<uuid>") results in exp=.../~hmac=...

Environment

  • Library version: 3.1.0
  • Language/framework version: Python 3.8
  • OS version: macOS 13.0.1

bad error handling/logging

Could you please add better logging around this LOC:

https://github.com/uploadcare/pyuploadcare/blob/master/pyuploadcare/dj/models.py#L65

Specifically log out what the raised exception is? Currently i get a frontend error of:
The file could not be found in your Uploadcare project
but in reality the error is:
*** InvalidRequestError: {"detail":"Date should be within 0:15:00 of our server time 2017-03-20 16:43:33.977836+00:00"}

which really doesn't help when trying to figure out why things aren't saving.

Thanks!

Bump httpx version

The conflict is caused by:
The user requested httpx==0.24.1
pyuploadcare 4.0.0 depends on httpx<0.24.0 and >=0.23.0; python_version >= "3.7" and python_version < "4.0"

Update webhooks API (event types and versioning)

New event types. Current enum:

  • file.uploaded
  • file.infected (it will be deprecated in favor of info_upldated in the future updates)
  • file.stored
  • file.deleted
  • file.info_updated

https://uploadcare.com/docs/webhooks/#event-types

image

Also, please check the versioning. It's easier to just send current API version, because from within API 0.7 you can't create 0.6 anyways (but you'll be able to create 0.8 when they it is released).

image

Payload is also updated (new initiator field), but since it's not part of the library, nothing is to update here.

p.s. Don't forget to update uploadcare-php-example if necessary.

Depricate ensure_on_cdn

Я не понимаю что делает и зачем нужен пользователям метод File.ensure_on_cdn(). Еще я не понимаю, зачем pyuploadcare вообще знать о cdn. Если файл is_ready и is_public, это все что нужно.

Consider a helper method, to remove all files in a group.

Brief description of the feature and motivation behind it

Motivated by #196

Groups are unremovable. But the files in the group are. Some times user has a need to remove all files in a group and currently, the only way to do this is to iterate over files and use delete or batch delete method.
This makes code more complex and, potentially, increases number of requests to achieve the goal.

p.s.: Also, consider adding this as a core REST API functionality?

Broken UUID broke whole section on admin side

If we get broken UUID which don't pass UUID_WITH_EFFECTS_REGEX check - then we got 500 error in production because:

class FileField(six.with_metaclass(models.SubfieldBase, models.Field)):
    """Django model field that stores uploaded file as Uploadcare CDN url.
    """
    def to_python(self, value):
        if value is None or value == '':
            return value

        if isinstance(value, File):
            return value

        if not isinstance(value, six.string_types):
            raise ValidationError(
                'Invalid value for a field: string was expected'
            )

        try:
            return File(value)
        except InvalidRequestError as exc:
            raise ValidationError(
                'Invalid value for a field: {exc}'.format(exc=exc)
            )

Raised ValidationError from to_python method bring us 500 error.

Simple case (used test_project):

from gallery.models import *

photo = Photo.objects.all()[0]
photo.arbitrary_file.uuid += 'ad'  # Will be nice if we raise exception in this point
photo.save()

And now if we try to open just section /admin/gallery/photo/- we catch error page.

I think we need change File.uuid to descriptor. Then we get full control over changing it value:

photo.arbitrary_file.uuid += 'ad'  # exception. We can't change it on invalid value

Unable to make an image field optional

Code:

class Course(models.Model):
    title = models.CharField(blank=False, max_length=255)
    desc = models.TextField(blank=False)
    number_of_hours = models.IntegerField(blank=False)
    instructor = models.ForeignKey('Instructor', blank=True, null=True, on_delete=models.SET_NULL)
    image = ImageField(null=True, blank=True, manual_crop="")
    
    def __str__(self):
        return self.title

Output:
image

Using:

  • Django version 2.2.6
  • Python 3.6
  • Form rendered with django_crispy_forms v1.8 using Bootstrap 4

crop images

Is it possible to specify a crop of the image you want from the saved image from django

Split file.copy method to local and remote copy methods

We should create two separate copy methods. Unfortunately, we have confusing API on REST side, but we don't have to copy them as is. In fact, we have two copy operations: copy to UC storage and copy to some target. For the local copy we need effects and store arguments, for the remote copy we need effects , make_public, pattern.

'instancemethod' object is not subscriptable

I just set up a new project using Django 1.5c1 and I'm getting an exception when I try to save a photo from admin:



Request Method: POST
Request URL: http://127.0.0.1:8000/admin/catalog/jewel/add/

Django Version: 1.5c1
Python Version: 2.7.2
Installed Applications:
('django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.admin',
 'potato.catalog',
 'south')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware')


Template error:
In template /Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 19
   'instancemethod' object is not subscriptable
   9 :             {% for field in line %}


   10 :                 <div{% if not line.fields|length_is:'1' %} class="field-box{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}"{% endif %}>


   11 :                     {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %}


   12 :                     {% if field.is_checkbox %}


   13 :                         {{ field.field }}{{ field.label_tag }}


   14 :                     {% else %}


   15 :                         {{ field.label_tag }}


   16 :                         {% if field.is_readonly %}


   17 :                             <p>{{ field.contents|linebreaksbr }}</p>


   18 :                         {% else %}


   19 :                              {{ field.field }} 


   20 :                         {% endif %}


   21 :                     {% endif %}


   22 :                     {% if field.field.help_text %}


   23 :                         <p class="help">{{ field.field.help_text|safe }}</p>


   24 :                     {% endif %}


   25 :                 </div>


   26 :             {% endfor %}


   27 :         </div>


   28 :     {% endfor %}


   29 : </fieldset>


Traceback:
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  140.                     response = response.render()
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/response.py" in render
  105.             self.content = self.rendered_content
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/response.py" in rendered_content
  82.         content = template.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  140.             return self._render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in _render
  134.         return self.nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  124.         return compiled_parent._render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in _render
  134.         return self.nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  124.         return compiled_parent._render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in _render
  134.         return self.nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  63.             result = block.nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  63.             result = block.nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  188.                         nodelist.append(node.render(context))
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  167.             return self.render_template(template, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/loader_tags.py" in render_template
  138.         output = template.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  140.             return self._render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in _render
  134.         return self.nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  188.                         nodelist.append(node.render(context))
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  188.                         nodelist.append(node.render(context))
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  156.         return self.render_template(self.template, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/loader_tags.py" in render_template
  138.         output = template.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  140.             return self._render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in _render
  134.         return self.nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  188.                         nodelist.append(node.render(context))
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  188.                         nodelist.append(node.render(context))
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  284.                 return nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  284.                 return nodelist.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/base.py" in render
  830.                 bit = self.render_node(node, context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render_node
  74.             return node.render(context)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/template/debug.py" in render
  87.             output = force_text(output)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/utils/encoding.py" in force_text
  99.                 s = s.__unicode__()
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/forms/forms.py" in __str__
  411.         return self.as_widget()
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/django/forms/forms.py" in as_widget
  458.         return widget.render(name, self.value(), attrs=attrs)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/pyuploadcare/dj/forms.py" in render
  36.             if value.url:
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/pyuploadcare/file.py" in __getattribute__
  29.         return super(File, self).__getattribute__(name)
File "/Users/xtr/.virtualenvs/potato/lib/python2.7/site-packages/pyuploadcare/file.py" in url
  132.         return self.info['original_file_url']

Exception Type: TypeError at /admin/catalog/jewel/add/
Exception Value: 'instancemethod' object is not subscriptable```

File Uploader (beta)

Brief description of the feature and motivation behind it

Greetings dear Uploadcare fellows.
I am trying out your product, and it indeed seems like a fantastic tool for my product needs.
I see there is a new "File Uploader (beta)" JS widget that is looking quite promising.
So, I wonder, what are the plans for this Python library? Is there a plan to switch to that version at some point?

Currently, I see it is pinned to the old widget with outdated version 3.7.4.

This question comes because I am using Python/Django stack, and it would be quite neat to be able to use this package with fully working integration instead of doing APIs via client myself.

I'd also be happy to contribute to that work in case it is a desired way of going, and Uploadcare will be the final choice of mine.

Cheers,
Rust

Model and Form ImageFields should support image shrinking

Since the upload widget can normally be configured to shrink an image via the "data-image-shrink" attribute, it would be helpful to be able to configure a field to set this automatically rather than having to resort to custom admin code to inject it into the widget everywhere it is required.

Add Secure Uploads functionality to Django Admin

Brief description of the feature and motivation behind it

Currently, if secure uploads are enabled in the project, uploads will fail in django admin.
We can provide signatures to the admin page, if secure uploads are enabled.

Allow to pass a UUID + params when generating signed URL (ACL)

Brief description of the feature and motivation behind it

Currently, the AkamaiSecureUrlBuilder class allows for signing a whole URL. In some cases, there's a need to generate a universal ACL token with wildcards. For example, we can specify an ACL as follows:

/{uuid}/*

and the token generated will be applicable to any variation of the file associated with the {uuid}. E.g., https://ucarecdn.com/{uuid}/-/resize/360x/-/format/auto/

See https://github.com/akamai/EdgeAuth-Token-Python/blob/master/akamai/edgeauth/edgeauth.py#L183 for reference.

API changed?

when i resize (500x300) a smaller image i get a stretched one to 500x300. I'm pretty sure this wasn't the case few days ago and IMHO it was a "better" solution to leave the image "as is" when it's smaller.

Fix functional tests that rely on network and expect some files in the project

Describe the bug

All functional tests should work offline.

Otherwise we're testing something else.
This particular test is source of intermittent build failures.

Expected behavior

functional tests should pass without active Internet connection.

Code / screenshots

FAILED tests/functional/resources/test_resources.py::test_file_info_has_new_structure - httpx.ConnectError: [Errno -5] No address associated with hostname

Update pydandic version to use the last 2.x

Brief description of the feature and motivation behind it

I tried to install the library inside a recend fastapi project and the poetry add pyuploadcare command fails because it pydantic higher than 2 is not supported.

Maintened project can run with last library version, it would be nice to have this one close to the last version.

WDYT? Thanks

❯ poetry add "pyuploadcare"
Configuration file exists at /Users/vincent.schoener/Library/Preferences/pypoetry, reusing this directory.

Consider moving TOML configuration files to /Users/vincent.schoener/Library/Application Support/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Using version ^4.2.1 for pyuploadcare

Updating dependencies
Resolving dependencies... (0.1s)

    Because no versions of pydantic match >1.8.2,<1.9.0 || >1.9.0,<1.9.1 || >1.9.1,<1.9.2 || >1.9.2,<1.10.0 || >1.10.0,<1.10.1 || >1.10.1,<1.10.2 || >1.10.2,<1.10.4 || >1.10.4,<1.10.5 || >1.10.5,<1.10.6 || >1.10.6,<1.10.7 || >1.10.7,<1.10.8 || >1.10.8,<1.10.9 || >1.10.9,<1.10.10 || >1.10.10,<1.10.11 || >1.10.11,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0
 and pydantic (1.8.2) depends on pydantic (1.8.2), pydantic (>=1.8.2,<1.9.0 || >1.9.0,<1.9.1 || >1.9.1,<1.9.2 || >1.9.2,<1.10.0 || >1.10.0,<1.10.1 || >1.10.1,<1.10.2 || >1.10.2,<1.10.4 || >1.10.4,<1.10.5 || >1.10.5,<1.10.6 || >1.10.6,<1.10.7 || >1.10.7,<1.10.8 || >1.10.8,<1.10.9 || >1.10.9,<1.10.10 || >1.10.10,<1.10.11 || >1.10.11,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0) requires pydantic (1.8.2).
    And because pydantic (1.9.0) depends on pydantic (1.9.0)
 and pydantic (1.9.1) depends on pydantic (1.9.1), pydantic (>=1.8.2,<1.9.2 || >1.9.2,<1.10.0 || >1.10.0,<1.10.1 || >1.10.1,<1.10.2 || >1.10.2,<1.10.4 || >1.10.4,<1.10.5 || >1.10.5,<1.10.6 || >1.10.6,<1.10.7 || >1.10.7,<1.10.8 || >1.10.8,<1.10.9 || >1.10.9,<1.10.10 || >1.10.10,<1.10.11 || >1.10.11,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1).
    And because pydantic (1.9.2) depends on pydantic (1.9.2)
 and pydantic (1.10.0) depends on pydantic (1.10.0), pydantic (>=1.8.2,<1.10.1 || >1.10.1,<1.10.2 || >1.10.2,<1.10.4 || >1.10.4,<1.10.5 || >1.10.5,<1.10.6 || >1.10.6,<1.10.7 || >1.10.7,<1.10.8 || >1.10.8,<1.10.9 || >1.10.9,<1.10.10 || >1.10.10,<1.10.11 || >1.10.11,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0).
    And because pydantic (1.10.1) depends on pydantic (1.10.1)
 and pydantic (1.10.2) depends on pydantic (1.10.2), pydantic (>=1.8.2,<1.10.4 || >1.10.4,<1.10.5 || >1.10.5,<1.10.6 || >1.10.6,<1.10.7 || >1.10.7,<1.10.8 || >1.10.8,<1.10.9 || >1.10.9,<1.10.10 || >1.10.10,<1.10.11 || >1.10.11,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0 || 1.10.1 || 1.10.2).
    And because pydantic (1.10.4) depends on pydantic (1.10.4)
 and pydantic (1.10.5) depends on pydantic (1.10.5), pydantic (>=1.8.2,<1.10.6 || >1.10.6,<1.10.7 || >1.10.7,<1.10.8 || >1.10.8,<1.10.9 || >1.10.9,<1.10.10 || >1.10.10,<1.10.11 || >1.10.11,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0 || 1.10.1 || 1.10.2 || 1.10.4 || 1.10.5).
    And because pydantic (1.10.6) depends on pydantic (1.10.6)
 and pydantic (1.10.7) depends on pydantic (1.10.7), pydantic (>=1.8.2,<1.10.8 || >1.10.8,<1.10.9 || >1.10.9,<1.10.10 || >1.10.10,<1.10.11 || >1.10.11,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0 || 1.10.1 || 1.10.2 || 1.10.4 || 1.10.5 || 1.10.6 || 1.10.7).
    And because pydantic (1.10.8) depends on pydantic (1.10.8)
 and pydantic (1.10.9) depends on pydantic (1.10.9), pydantic (>=1.8.2,<1.10.10 || >1.10.10,<1.10.11 || >1.10.11,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0 || 1.10.1 || 1.10.2 || 1.10.4 || 1.10.5 || 1.10.6 || 1.10.7 || 1.10.8 || 1.10.9).
    And because pydantic (1.10.10) depends on pydantic (1.10.10)
 and pydantic (1.10.11) depends on pydantic (1.10.11), pydantic (>=1.8.2,<1.10.12 || >1.10.12,<1.10.13 || >1.10.13,<2.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0 || 1.10.1 || 1.10.2 || 1.10.4 || 1.10.5 || 1.10.6 || 1.10.7 || 1.10.8 || 1.10.9 || 1.10.10 || 1.10.11).
    And because pydantic (1.10.12) depends on pydantic (1.10.12)
 and pydantic (1.10.13) depends on pydantic (1.10.13), pydantic (>=1.8.2,<2.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0 || 1.10.1 || 1.10.2 || 1.10.4 || 1.10.5 || 1.10.6 || 1.10.7 || 1.10.8 || 1.10.9 || 1.10.10 || 1.10.11 || 1.10.12 || 1.10.13).
    Because no versions of pyuploadcare match >4.2.1,<5.0.0
 and pyuploadcare (4.2.1) depends on pydantic (>=1.8.2,<2.0.0), pyuploadcare (>=4.2.1,<5.0.0) requires pydantic (>=1.8.2,<2.0.0).
(1) Thus, pyuploadcare (>=4.2.1,<5.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0 || 1.10.1 || 1.10.2 || 1.10.4 || 1.10.5 || 1.10.6 || 1.10.7 || 1.10.8 || 1.10.9 || 1.10.10 || 1.10.11 || 1.10.12 || 1.10.13).

    Because no versions of pydantic-settings match >2.0.3,<2.1.0 || >2.1.0,<3.0.0
 and pydantic-settings (2.0.3) depends on pydantic (>=2.0.1), pydantic-settings (>=2.0.3,<2.1.0 || >2.1.0,<3.0.0) requires pydantic (>=2.0.1).
    And because pydantic-settings (2.1.0) depends on pydantic (>=2.3.0), pydantic-settings (>=2.0.3,<3.0.0) requires pydantic (>=2.0.1).
    And because pyuploadcare (>=4.2.1,<5.0.0) requires pydantic (1.8.2 || 1.9.0 || 1.9.1 || 1.9.2 || 1.10.0 || 1.10.1 || 1.10.2 || 1.10.4 || 1.10.5 || 1.10.6 || 1.10.7 || 1.10.8 || 1.10.9 || 1.10.10 || 1.10.11 || 1.10.12 || 1.10.13) (1), pyuploadcare (>=4.2.1,<5.0.0) is incompatible with pydantic-settings (>=2.0.3,<3.0.0)
    So, because cloudinary-back-poc depends on both pydantic-settings (^2.0.3) and pyuploadcare (^4.2.1), version solving failed.

Image not deleted

Assume we have a Photo model like this:

# myapp/models.py
from pyuploadcare.dj import ImageField
class Photo(models.Model):
    image = ImageField(validators=[size_validator, extension_validator])

Then we delete it from our database. But file in cdn still alive. It easy to reproduce:

>>> from myapp.models import *
>>> a = Photo.objects.get(pk=23)
>>> a.image.cdn_url
u'https://ucarecdn.com/1ada3776-40fd-4193-8aa1-00955a50a26c/'
>>> a.delete()
>>> a.pk
>>> a.image.cdn_url
u'https://ucarecdn.com/1ada3776-40fd-4193-8aa1-00955a50a26c/'

Photo object was successful deleted from database. But image.cdn_url still active.

The solution what i see is subscribe on post_delete signal inside Field.contribute_to_class method's and execute File.delete.

But i think in some cases it right behaviour (e.g. backup). For this case we can specify setting e.g. PYUPLOADCARE_REMOVE_FILES which will be turn on this functionality.

How can I migrate from normal ImageField?

I have a Django site currently running that hosts all of it's assets on S3, through django-storages. What is the migration pack look like to change over all my assets to use UploadCare?

getting links with image extensions

So far I've been using this without an issue but so far working on my twitter card integration which needs an image url and this app returns an image without the extension like .jpg I wonder if twitter isn't taking the link as a valid image url.
If so then it would be advantageous to be able to add this extension even if the backend simply understands what it's for an ignores it.

Update and refactor /transformations/image.py

Honestly, I don't like that we have to mirror URL API in the lib. Isn't there a more elegant way?

If not (or not now), let's reorder it to mirror the URL API structure better.

Changes/comments:

New stuff since the last update:

Proposal of new interface

I propose following pyuploadcare interface:

import pyuploadcare
pyuploadcare.conf.pub_key = 'blah'
pyuploadcare.conf.secret = 'secret'

file1 = pyuploadcare.File('1')
file1 = pyuploadcare.File('http://ucarecdn.com/1/')
file1 = pyuploadcare.File.construct_from(file_info={'uuid': 1, 'fizz': 'buzz'})

img = open('img.jpg')
file2 = pyuploadcare.File.upload(img)
file2.store()

file3 = pyuploadcare.File.upload_from_url('http://test.com/filename.txt')
file3.store()

group1 = pyuploadcare.FileGroup('1~3')
group1 = pyuploadcare.FileGroup('http://ucarecdn.com/1~3/')

group2 = pyuploadcare.FileGroup.create([file1, file2, file3])
group2.store()

delete filegroup

I might be missing something, but it appears there's no way to delete filegroups. I can delete all the individual files within a group (though not easily because if I reference I filegroup, pulling the UUIDs out for each image is not trivial), but once I do that I can't figure out how to delete the group itself. I know they're immutable, but can't they be deleted?

Also, it'd be nice to be able to delete all the files within the group, as well as the group itself in one function call.

Currently, I'm struggling to even delete the files withing a single filegroup. For security purposes, I'm trying to essentially delete the files for each of my Shopify store's orders as soon as I'm done processing it, Each order creates a single filegroup of images I use, so I really want to just delete all the files in a filegroup each time a particular function finishes its work. But sending the full cdn_urls for the files within the group to the delete_files method, as shown below, doesn't end up deleting anything. It doesn't throw any errors, but these files are still there after running it. Maybe the delete_files method should be updated to accept a list of cdn_urls instead of just lists of uuids?

file_groups = uploadcare.list_file_groups(limit=10)
for file_group in tqdm(file_groups):
    delete_these=file_group.file_cdn_urls
    uploadcare.delete_files(delete_these)

As far as I can tell, I'd need to dig into the info for each file to pull the UUID out, then delete them one by one, though I have yet to get that to actually work.

Update Widget to Version 3

Is it possible to update the widget now to the latest version (I.E. version 3.1.1 as of the time of writing)?

Relax `pytz` dependency constraint

Describe the bug

This isn't a bug per-say, but pytz is pinned to < 2022. This means that I can't use pyuploadcare alongside my code library if I want pytz >= 2022.

The conflict is caused by:
    The user requested pytz==2022.2
    pyuploadcare 3.0.0 depends on pytz<2022.0 and >=2021.1

To fix this you could try to:
1. loosen the range of package versions you've specified
2. remove package versions to allow pip attempt to solve the dependency conflict

ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts

Expected behavior

The pyuploadcare library should work with later pytz versions as well, so that we can have up to date timezone definitions even if pyuploadcare hasn't needed a release in a while.

Check api version

Нужно убедиться, что все поля из методов api, которые используются в pyupload, действительно объявлены в версии 0.2.

https://uploadcare.com/documentation/rest/#file

Было бы здорово так же переименовать и задеприкейтить свойство File.is_on_s3 и метод File.ensure_on_s3().

Fix broken test coverage check

Years ago there was a Coveralls integration set up and working. However, it has been broken for some time now and requires fixing.

Screenshot 2023-06-26 at 21 36 53

Things to consider:

  • Are there better alternatives to Coveralls available in 2023?

Break with Django2.0 Update

Hi,

I thought I would let you guys know that your python-django library breaks with the Django 2.0 update. The error:

AttributeError: 'ImageField' object has no attribute ‘_get_val_from_obj'

This happens when the ImageField is serialised through djangorest.

‘_get_val_from_obj' is depreciated and removed In Django2.0.

James

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.