All Versions
10
Latest Version
Avg Release Cycle
8 days
Latest Release
1492 days ago

Changelog History

  • v7.0.0.a5 Changes

    February 27, 2020

    ๐Ÿš€ THIS RELEASE HAS MANY BREAKING CHANGES AND IS AN ALPHA!

    ๐Ÿš€ Also this is a draft for the release notes. I'll be updating it in the coming days.

    โš  WARNING

    ๐Ÿš€ This is an alpha release! Most of the extensions will be broken at this point.
    ๐Ÿš€ Who this release is for:

    • ๐Ÿฑ Alpha testers (would be great if you could be one of them โค๏ธ );
    • Extension devs (if you have a thumbor extension now is a great time to modernize it to py3 and thumbor 7).

    Installing

    ๐Ÿš€ Since this release is an alpha release if you want to test it you must specify the version when using pip:

    $ pip install "thumbor==7.0.0a5"
    $ thumbor-doctor
    

    Keep on reading for the changes you need to be aware of. If you experience any problems, please run thumbor-doctor --nocolor and create an issue with your problem and the results of running thumbor-doctor.

    Introduction

    ๐Ÿš€ This release notes will be slightly different from others as I feel we need to have more of a conversation on what happened. I'll try to detail the changes below, but first I'll explain them.

    ๐Ÿš€ We set out to update thumbor to work with python3. During the work required to do that, it became clear that we were going to break most extensions with this release.

    โšก๏ธ At this point we made a decision to not only update thumbor to python3, but completely modernize it.

    Some of the 7.0.0 features:

    • ๐Ÿ‘ Python 3.6+ support. Python 2 is not supported anymore (does not even work).
    • ๐Ÿš€ Updated tornado to release 6.0.3. This should fix many bugs/vulnerabilities we still had due to old tornado.
    • โšก๏ธ Updated pillow version to 7.0.0.
    • โœ… All tests now run using py.test.
    • โšก๏ธ Updated every dependency we could update.

    โšก๏ธ While updating the codebase to support new tornado and python 3 we also decided to get rid of the callback hell that tormended the committers for the last 10 years or so. The new codebase uses proper async/await idioms for all the asynchronous code.

    As the last part of the intro, we added a new concept to customizing thumbor: handler lists. It has been a request for a long time to allow thumbor extensions to add new handlers to thumbor. Well, now you can!

    Lets get to the details!

    ๐Ÿ’ฅ Breaking Changes

    ๐Ÿ‘€ First let's see what breaks (apart from not supporting python 2, that is).

    Storages

    Previously storages were implemented like this (this is No Storage):

    class Storage(BaseStorage): def put(self, path, bytes): return path def put\_crypto(self, path): return path def put\_detector\_data(self, path, data): return path @return\_futuredef get\_crypto(self, path, callback): callback(None) @return\_futuredef get\_detector\_data(self, path, callback): callback(None) @return\_futuredef get(self, path, callback): callback(None) @return\_futuredef exists(self, path, callback): callback(False) def remove(self, path): pass
    

    ๐Ÿš€ With release 7.0.0 we modernize these functions to be async. The same storage in the new release:

    class Storage(BaseStorage): async def put(self, path, file\_bytes): return path async def put\_crypto(self, path): return path async def put\_detector\_data(self, path, data): return path async def get\_crypto(self, path): return Noneasync def get\_detector\_data(self, path): return Noneasync def get(self, path): return Noneasync def exists(self, path): return Falseasync def remove(self, path): pass
    

    โšก๏ธ Much simpler, right? No funky callback business. The downside is that all storages out there need to be updated. It should be simply a matter of "asyncifying" them.

    Loaders

    ๐Ÿ‘€ The same applies to loaders. Let's see what a loader looked like in 6.7.2:

    @return\_futuredef load(context, path, callback): # does a lot of stuff and :( callback(loaded\_image)
    

    Now in 7.0.0 it is a simple async function:

    async def load(context, path): # does a lot of stuff with no callbacks whatsoeverreturn loader\_result
    

    It's much simpler to understand the new loaders (and to create new ones), but the same issue as above: previous loaders won't work with thumbor 7.0.0+ (until asyncified).
    โšก๏ธ Important note: going forward, loaders should return LoaderResult object. While returning plain image bytes still works, it's encouraged to update loaders to return object.

    Result Storages

    You get the gist, right? No Storage Result Storage in 6.7.2:

    class Storage(BaseStorage): # THIS METHOD IS NOT EVEN ASYNC :(def put(self, bytes): return ''@return\_futuredef get(self, callback): callback(None)
    

    Now in 7.0.0+:

    class Storage(BaseStorage): async def put(self, image\_bytes): return ""async def get(self): return None
    

    Previous result storages won't work with thumbor 7.0.0+ (until asyncified).

    Filters

    Same ol', same ol'. Let's check format filter in 6.7.2.

    class Filter(BaseFilter): @filter\_method(BaseFilter.String) def format(self, format): if format.lower() not in ALLOWED\_FORMATS: logger.debug('Format not allowed: %s' % format.lower()) self.context.request.format = Noneelse: logger.debug('Format specified: %s' % format.lower()) self.context.request.format = format.lower()
    

    Now the same filter in 7.0.0:

    class Filter(BaseFilter): @filter\_method(BaseFilter.String) async def format(self, file\_format): if file\_format.lower() not in ALLOWED\_FORMATS: logger.debug("Format not allowed: %s", file\_format.lower()) self.context.request.format = Noneelse: logger.debug("Format specified: %s", file\_format.lower()) self.context.request.format = file\_format.lower()
    

    Hardly noticeable, right? Except now you get to run async code in your filters without arcane magic or callbacks. As with all the others, previous filters won't work with thumbor 7.0.0+ (until asyncified).

    Detectors

    These were also modernized. Let's take a look at our face detector in 6.7.2:

    class Detector(CascadeLoaderDetector): # details removed for claritydef detect(self, callback): features = self.get\_features() if features: for (left, top, width, height), neighbors in features: top = self.\_\_add\_hair\_offset(top, height) self.context.request.focal\_points.append( FocalPoint.from\_square(left, top, width, height, origin="Face Detection") ) callback() else: self.next(callback)
    

    Now the same detector in 7.0.0+:

    class Detector(CascadeLoaderDetector): # details removed for clarityasync def detect(self): features = self.get\_features() if features: for (left, top, width, height), \_ in features: top = self.\_\_add\_hair\_offset(top, height) self.context.request.focal\_points.append( FocalPoint.from\_square( left, top, width, height, origin="Face Detection" ) ) returnawait self.next()
    

    โšก๏ธ No more callbacks and now detector pipeline works by awaiting the next detector or just a plain early return to stop the pipe. As with the other updates, previous detectors will not work with thumbor 7.0.0 (until asyncified).

    ๐Ÿ‘Œ Improvements

    โœ… Thumbor Testing Tools

    โœ… This one is for you, an extension author! Thumbor now includes a thumbor.testing module that ships with it and allows extensions to create tests that get thumbor up and running easier. An example test from our code:

    from preggy import expectfrom tornado.testing import gen\_testfrom thumbor.testing import TestCasefrom thumbor.storages.no\_storage import Storage as NoStorageclass NoStorageTestCase(TestCase): def get\_image\_url(self, image): return "s.glbimg.com/some/{0}".format(image) @gen\_testasync def test\_store\_image\_should\_be\_null(self): iurl = self.get\_image\_url("source.jpg") storage = NoStorage(None) stored = await storage.get(iurl) expect(stored).to\_be\_null() # many more tests!
    

    โœ… Notice how the test can now test only what it needs to. We're using py.test now and that allows async test methods.

    ๐Ÿ“„ Revised Docs

    ๐Ÿ“„ The many fundamental changes in thumbor prompted a major review of the docs and improvement of many areas in it.

    ๐Ÿ– Handler Lists

    Not all is breaking changes, though. Now you get to extend thumbor with new handlers! And it is as simple as creating a module with:

    from typing import Any, cast from thumbor.handler\_lists import HandlerList from my.handlers.index import IndexHandler def get\_handlers(context: Any) -\> HandlerList: something\_enabled = cast(bool, self.context.config.SOMETHING\_ENABLED) if not something\_enabled: return [] return [(r"/my-url/?", IndexHandler, {"context": self.context}),]
    

    Then including it in thumbor.conf:

    from thumbor.handler\_lists import BUILTIN\_HANDLERS# Two things worth noticing here:# 1) The handler list order indicates precedence, so whatever matches first will be executed;# 2) Please do not forget thumbor's built-ins or you'll kill thumbor functionality.HANDLER\_LISTS = BUILTIN\_HANDLERS + ["my.handler\_list',]
    

    Acknowledgements

    ๐Ÿš€ This release would not be possible without the work of thumbor's commiters. I'd like to call out the work of @kkopachev that has enabled me to move forward with this release.

    ๐Ÿš€ Again thanks @kkopachev for all the code reviews and thanks the whole of thumbor community for catching errors when I hadn't even written the release notes! That's nothing short of amazing.

    ๐Ÿš€ Now onto the not comprehensive list of changes in this release!

    ๐Ÿ›  Fixes/Features

    • ๐Ÿ›  Fix typo (thanks @timgates42);
    • ๐Ÿ‘ Python 3 support (thanks @kkopachev);
    • โœ‚ Removed callbacks in favor of async-await;
    • Simplified pillow feature checks (thanks @kkopachev);
    • ๐Ÿ‘• Extensive work to improve codebase quality (lint errors should be few now);
    • Using stdlib which instead of a custom one (thanks @kkopachev);
    • ๐Ÿ“š Major documentation review;
    • โž• Added handler lists to allow further extension;
    • โšก๏ธ Update classifiers and add python_requires to help pip (thanks @hugovk);
    • Ability to run thumbor in multiprocess mode (thanks @kkopachev);
    • ๐Ÿ‘ Python 3.6 support (thanks @amanagr);
    • ๐Ÿ†• New thumbor-doctor command to help diagnose issues with installs.

    ๐Ÿš€ Diff to previous release
    ๐Ÿš€ Pypi release

  • v7.0.0.a4

    March 09, 2020
  • v7.0.0.a3

    March 09, 2020
  • v7.0.0.a2

    February 27, 2020
  • v7.0.0.a1

    February 26, 2020
  • v6.7.5 Changes

    March 09, 2020

    ๐Ÿ”„ Changelog

    ๐Ÿ“„ Docs

    • ๐Ÿ›  Fixed issue with packaging in 6.7.3.

    ๐Ÿš€ Diff to previous release
    ๐Ÿš€ Pypi release

  • v6.7.4

    March 09, 2020
  • v6.7.3 Changes

    February 29, 2020

    ๐Ÿ”„ Changelog

    ๐Ÿ“„ Docs

    • ๐Ÿ›  Fixed issue with running thumbor with a file descriptor (thanks @kkopachev).

    ๐Ÿš€ Diff to previous release
    ๐Ÿš€ Pypi release

  • v6.7.2 Changes

    February 26, 2020

    ๐Ÿ”„ Changelog

    ๐Ÿ“„ Docs

    • ๐Ÿ›  Fixed documentation build issues.

    ๐Ÿš€ Diff to previous release
    ๐Ÿš€ Pypi release

  • v6.7.1 Changes

    February 03, 2020

    ๐Ÿ”„ Changelog

    ๐Ÿ›  Fixes

    • ๐Ÿ›  Fix for a bug that blocked tornado thread (#1151 thanks @Tideorz);
    • ๐Ÿ›  Fix numpy not writable ndarray (#1171 thanks @cristiandean);
    • ๐Ÿ›  Fix FocalPoint from_dict now returns integer (#1172 thanks @cristiandean);
    • Replace dots in original image's source with underscore to prevent stats hierarchy levels being created (#1241 thanks @dfrieling).

    ๐Ÿ‘Œ Improvements

    Modernizing

    ๐Ÿ“„ Docs

    • โž• Added thumbor_spaces plugin (#1158 thanks @siddhartham);
    • โšก๏ธ Updated contributing.md (thanks @marcelometal);
    • ๐Ÿ›  Fix readme.mkd badges (#1177 thanks @cristiandean);
    • Update MAX/MIN_WIDTH and MAX/MIN_HEIGHT configuration docs (#1183 thanks @HectorNM);
    • Update how_to_upload_images.rst (thanks @codechelon);
    • โšก๏ธ Update thumbor logo in docs (#1225 thanks @matgomes);
    • ๐Ÿ›  Fix link to blog post of Squareup in docs (#1250 thanks @ad-m);
    • โž• Add 'thumbor-video-engine' to plugin documentation (#1247 thanks @fdintino);
    • ๐Ÿ›  Fix simple typo: transparant -> transparent (#1244 thanks @timgates42).

    ๐Ÿš€ Diff to previous release
    ๐Ÿš€ Pypi release