Tuesday, August 5, 2008

Photoshop scripting with Python

Photoshop natively supports scripting with AppleScript, JavaScript and VBScript. While Python is notably absent from that list, it can still be used to automate nearly anything in Photoshop. This is thanks to the extensive COM interface Photoshop provides.

The methods here are similar to those used in my GDC 2008 Python lecture, about driving 3ds Max via Python. You start by dispatching the Photoshop COM server, using Python as the client:

import win32com.client
psApp = win32com.client.Dispatch("Photoshop.Application")
This connects to your already-opened Photoshop session, or opens one if none are running. The root COM object is then assigned to psApp, and you're ready to do some cool stuff. Here's a quick example:
psApp.Open(r"D:\temp\blah.psd")         # Opens a PSD file
doc = psApp.Application.ActiveDocument  # Get active document object
layer = doc.ArtLayers[0]                # Get the bottom-most layer
layer.AdjustBrightnessContrast(20,-15)  # Bright +20, Contrast -15
doc.Save()                              # Save the modified PSD
Here's a more complex example. This script recursively scans a folder for PSD files, exporting various textures contained inside. One PSD can have specifically-named Layer Groups, each of which is written to a separate PNG file with a specific suffix. If a Group contains several layers, they're flattened when exported, allowing you to keep all your layered effects intact in the PSD.

In the example below, a group named "diffuse" is exported as "psdname_D.png", the "normal" group as "psdname_N.png", and so on. The exportType dictionary determines the name/suffix pairs.
# Recursively scans a folder (psdRoot) for Photoshop PSD files.
# For each, exports various 24-bit PNG textures based on layer
# groups found in the PSD.
# Requires the Win32 Extensions:
# http://python.net/crew/mhammond/win32/

import win32com.client
import os

# Change to match your root folder
psdRoot = r'C:\ArtFiles\PSD'

# Map of layer group names and the suffixes to use when exporting
exportTypes = {'diffuse':'_D', 'normal':'_N', 'specular':'_S'}

if (__name__ == '__main__'):
   # COM dispatch for Photoshop
   psApp = win32com.client.Dispatch('Photoshop.Application')

   # Photoshop actually exposes several different COM interfaces,
   # including one specifically for classes defining export options.
   options = win32com.client.Dispatch('Photoshop.ExportOptionsSaveForWeb')
   options.Format = 13   # PNG
   options.PNG8 = False  # Sets it to PNG-24 bit

   # Get all PSDs under root dir
   psdFiles = []

   for root, dir, files in os.walk(psdRoot):
      for thisFile in files:
         if (thisFile.lower().endswith('.psd')):
            fullFilename = os.path.join(root, thisFile)

   # Loop through PSDs we found
   for psdFile in psdFiles:
      doc = psApp.Open(psdFile)
      layerSets = doc.LayerSets

      if (len(layerSets) > 0):
         # First hide all root-level layers
         for layer in doc.Layers:
            layer.Visible = False
         # ... and layerSets
         for layerSet in layerSets:
            layerSet.Visible = False
         # Loop through each LayerSet (aka Group)
         for layerSet in layerSets:
            lsName = layerSet.Name.lower()

            if (lsName in exportTypes):
               layerSet.Visible = True  # make visible again

               # Make our export filename
               pngFile = os.path.splitext(psdFile)[0] + exportTypes[lsName] + '.png'

               # If PNG exists but older than PSD, delete it.
               if (os.path.exists(pngFile)):
                  psdTime = os.stat(psdFile)[8]
                  pngTime = os.stat(pngFile)[8]
                  if (psdTime > pngTime):

               # Export PNG for this layer Group
               if (not os.path.exists(pngFile)):
                  doc = psApp.Open(psdFile)
                  doc.Export(ExportIn=pngFile, ExportAs=2, Options=options)
                  print 'exporting:', pngFile
                  print 'skipping newer file:', psdFile
               # Make LayerSet invisible again
               layerSet.Visible = False

         # Close PSD without saving
It only exports when the PNG is missing or older than the PSD. This makes it good for running a batch texture export on your project's entire texture tree.

Here is a ZIP containing the above script and a sample PSD file to try it on: exportTextureLayers.zip (143 KB)

I imagine you can do all of the above with the native Photoshop scripting. I just think it's cool being able to use Python instead of rooting through a language I'm less familiar with. Dinosaurs were roaming the earth the last time I tried anything in VB.

If you dig this, I'd recommend reading the Photoshop CS5 Scripting Guide and Photoshop CS5 VBScript Reference found in the Adobe Photoshop Developer Center. While the above wasn't VBScript, the COM interface we used is nearly identical.


Anonymous said...

Just discovering Python and COM interfaces. wrapping my head around these things. Your blog has been a great insight into creating great studio tools!

Anonymous said...

In Mac OS X you can also script Photoshop with Python by using appscript:


It's great for people who don't like the AppleScript syntax, but who do like the power of automation.

Nico Dufort said...

Not sure if anyone is still reading this, but while I get the basic stuff to work (create new doc, add layers, change names, types, etc.), I can't get a selection to work. I get an error telling me that either I need a one dimensional array, or that my argument is illegal.
This won't work: myDoc.Selection.Select([...8-number array to define selection area...])

Currently at work, so I will also try at home in case something is wrong here, but has anyone gotten this to work?


Nico Dufort said...

After a day of failing to get it to work, I found comtypes (http://sourceforge.net/projects/comtypes/) which solved the problem of passing a multi dimensional array for selection. I still have to test the rest, but that's a start....

Just in case it can help others.

Adam Pletcher said...

Great, thanks for that update, nico!

We found that same issue with the Photoshop Select method, and also worked around it using comtypes. Glad to hear we weren't alone.

Nico Dufort said...

Me again. I managed to get a lot done with some basic Python calls to automate document creation in Photoshop.

My current problem: when I run the script from IDLE, everything works like a charm, and everything I tell Photoshop to do is happening.

When I call my script from inside Photoshop, it just stops for no appearant reason. The Python code is called through an Action that calls a one-liner Javascript. I use this method for my other scripts, and I never got a problem.

My question: What would be a good way to see what errors are happening when running my script inside Photoshop.

I tried a few write statements that are saved to a text file, but I cannot find a smart way to check for things (since I don't know what to check for). I'm not a programmer, so while the whole try/except statement business makes some sense, I just have no idea how to approach this. If it were throwing errors in IDLE, that would be great, but the fact that it works in one environment and not another leaves me clueless.

(I'm working with comtypes, not win32.com).

Cheers. nico.

Nico Dufort said...

I found the problem:

My python code is saved with the extension .pyw to prevent the console from popping up every time an artist would use the tool.

I was suggested to change the extension back to .py just to see if the console would show anything interesting. After doing so and calling the script from Photoshop, it worked fine.

Seeing all the normal output in the console made me think that maybe the verbose feedback through my print statements was too much. I commented them out, saved the file back to .pyw, and the script worked fine.

I think this comes maybe from the limitation of how Javascript is dealing with output to a console. I will try to uncomment the print statements one at a time to see how much I can print before it crashes again (my other scripts also had print statements to the console, and they work fine).

Lesson learned: when trying out the tools inside Photoshop, log to a file instead of the console.

Anonymous said...

nice post

Anonymous said...
This comment has been removed by a blog administrator.
Matt said...

Very nice. However it doesn't seem as though I can use dir() or help() on objects or methods?

Nico Dufort said...

Matt, are you getting any errors in your shell? While some of the info is not as verbose as your typical Python help, I can do a dir() help() on my objects.

Could you perhaps tell us what you get, and what setup you have?


Matt said...

sure. I'm on winXP using the win32com.client module, performing the following:

import win32com.client
psApp = win32com.client.Dispatch("Photoshop.Application")
print help(psApp)

I would expect to get the methods of the Photoshop.Application class but I'm getting the class instance of the com module which is pretty much:

etc ...

what am I missing here. I'd like to find a way to get the members of a particular class such as:

def AdjustBrightnessContrast(...)

etc ...

Thanks for the reply,

Adam Pletcher said...

COM objects don't generally offer that kind of reflection. I've never dug too deep into the particulars, but you can find more on that if you search for "early- and late-inding" for COM.

Also have a look at the "makepy" tool in pywin32, that sometimes provides a bit more info with help/dir.

Before you bother with that, have a look at the Adobe Photoshop VBScript Reference doc I linked to in the blog. It has a full list of commands for the COM interface.

Nico Dufort said...

Matt, as Adam suggested, if you lookup the PS docs and dig out the VBScript reference, you will have all the info you need.

I am not on a machine with Photoshop at the moment, but in terms of interactive feedback in your shell, I think if you have an active document, and a layer, you should be able to get a little help on those during your session.
So, if you have: doc = psApp.Application.ActiveDocument or layer = doc.ArtLayers[0], you should be able to get some help on these from your shell (dir/help). Again, I don't have this handy and it's been a while since I last did something, but definitively keep the VBScript Ref open when you work.

Adam Pletcher said...

Make that "early- and late-binding" in my comment above. I dropped the B somehow...

Matt said...

Cool. thanks for keeping the thread alive. I'll look at the pdf reference.

Thanks again

呱呱 said...

How do I load different versions of Photoshop installed on my machine?
seems only loads the latest one.

BTW, how do I know the name of a COM program to pass it to Dispatch.

Nico Dufort said...

While it is passed 3AM here and I am on linux at the moment, I would say that it depends on what/how Photoshop is registered.
I am not sure why you would want to call different versions of PS through your scripts, but the registry entry for Photoshop.Application should only point to one version, if I am correct, and Photoshop.Application.# should call the specified version (# being 1,2,3,etc.).

I'm pretty sure there is a smart way to have COM tell you the names of the app you want to call, but you can lookup the registry and it will show you all software and how to call them (sorry, not a big Windows user here).

For more serious answers on COM and Python, try to check out the O'Reilly Python for Windows book.

Anonymous said...

is there a way to get the python COM script, to tell Photoshop to run a .jsx script ?

Nico Dufort said...

I remember seeing something about ActionLists/Descriptors and such in the VBScript Reference for Photoshop. You might want to check it out to see if you can associate your jvscript code to an action in PS, and then call that action from Python. I have never tried it, and I'm on linux at the moment, but I'd be curious to see if this works.

Erik said...

found this great article:


python + comtypes + scriplistener
= long route to automate photoshop, so it can call a jsx from python:

def runJSX ( aFilePath ):
id60 = psApp.stringIDToTypeID ( "AdobeScriptAutomation Scripts" )
desc12 = comtypes.client.CreateObject('Photoshop.ActionDescriptor')
id61 = psApp.charIDToTypeID( "jsCt" )
desc12.putPath( id61, aFilePath )
id62 = psApp.charIDToTypeID( "jsMs" )
desc12.putString( id62, "null" )
psApp.executeAction( id60, desc12, 2 )

psApp = comtypes.client.CreateObject('Photoshop.Application')
runJSX ( "C:\\Program Files\\Adobe\\Adobe Photoshop CS3\\Scripting Guide\\Sample Scripts\\JavaScript\\OpenDocument.jsx" )

Nico Dufort said...

Neat... Actually, to the previous poster: when you want to achieve something that is not directly doable (i.e. no code equivalency), using the script listener is definitively a valuable tool; I used this a few times to create methods that were not found in VBScript. Thanks for the reminder Erik.

mistermatti said...

is there a way to call win32 from Maya? Can you point me to some documentation? :) Very appreciated!

P.S.: Great article, Adam!

Adam Pletcher said...

I'm not a big Maya user, but there's this thread over at TAO that seems to have some info on that. Specifically the post half-way down the first page from WHW that starts "It would appear...".

There are now x64 builds of pywin32 available now, so it may even work with Maya 64-bit.

mistermatti said...

@Adam thx a lot! Gonna have a shot at that :) Tech-Artists is a geat ressource!

Anonymous said...

hi..i want to open feather dialog box in photoshop but with the use of script not directly.ok.plz

Anonymous said...

Nice posting! psd development

Sunny Carter said...

Hi there,

Thanks for this post - I've used it as the base for a script to recolor PSD files.

It works fine using Python 2.6 but fails when I've upgraded to Python 3.0. I have installed pywin32-214.win32-py2.6.exe for Python 2.6 and pywin32-214.win32-py3.0.exe for use with Python 3.0

Subset of the script:
sorry if indentation wrong
import win32com.client
psApp = win32com.client.Dispatch('Photoshop.Application')
doc = psApp.Open(os.path.abspath(psd_image.psd_filename))
# Here I do the image manipulation which I haven't #included here as it is irrelevant (and working fine -
# calling Photoshop and making it do stuff)
psd_save_options = win32com.client.Dispatch("Photoshop.PNGSaveOptions")
psd_save_options.Interlaced = False
print("Save as %s" % (os.path.join(output_directory, psd_image.final_filename)))
doc.SaveAs(os.path.join(output_directory, psd_image.final_filename),
2) # lower case extension

Exception hit:

Unexpected error detail:
Traceback (most recent call last):
File "C:\brander\brander.py", line 2519, in reconstructPSDImageFile psd_save_options.Interlaced = False
File "C:\Program Files\Python31\lib\site-packages\win32com\client\dynamic.py", line 550, in __setattr__ self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, 'Adobe Photoshop' , 'Boolean value expected', None, 0, -2147220276), None)

Does anyone have any ideas how to solve? The problem must be with pywin32. I've tried using True, "False", 0 and 1 for the Boolean value and none work. I repeat that this worked fine on Python 2.6 but I need to upgrade to 3.0 for other reasons. I've also tried Python 3.1.1 and relevant pywin32 module with the same results. Any call using True or False across the API seems to fail.

Marco Bauriedel said...

This is a great base to get into controlling Photoshop through Python.
I`ve only one problem coming up, which is the question how to move a Layer down in a PSD File.

I`ve found the commands that should do it:

Move(ApplicationObject,InsertionLocation) or duplicate(ApplicationObject,InsertionLocation)

in combination with


0 (psPlaceInside)
1 (psPlaceAtBeginning)
2 (psPlaceAtEnd)
3 (psPlaceBefore)
4 (psPlaceAfter)

The object’s position in the Layers

This example works as far that the active layer is duplicated, but not into another file or another layer position:

layer = doc.ArtLayers[0]
layer.duplicate(doc, ElementPlacement.PLACEATBEGINNING)

Python doesn`t recognize the
ElementPlacement flag/command.

I tried to "load" it with
psPlacement = win32com.client.Dispatch("Photoshop.psElementPlacement")
psPlacement = win32com.client.Dispatch("Photoshop.ElementPlacement")
which didn`t work.

Any hint on how to get the ElementPlacement working is highly
appreciated. Thanks again Adam for posting this great intro to Photoshop scripting with Python.

mistermatti said...

Marco, have you had a look at this?


Just had a quick look but it might help you?

Marco Bauriedel said...

Hi MattiG,

I haven`t looked in detail over there, but just found out that my mistake was a simple matter of upper case/lower case-typing .

instead of

layerNew = doc.activeLayer
layerNew.Move (doc, 2)

I tried layerNew.move (doc, 2).

(2 stands for moving the layer below the bottom layer)

Everything works like a charm now.
Thanks alot for the link and your fast reply.

automotive hand tools said...

nice description... A co- worker said me that Photoshop with Python is a good combination... he recommended me your blog...
Good job

Adam Pletcher said...

Apologies to "Send flowers to Poland", it seems I accidentally deleted your comment. :/

I'm glad you enjoy the blog, thanks for the note!

etyhhdfgh said...

Just discovering Python and COM interfaces. wrapping my head around these things. Your blog has been a great insight into creating great studio tools!

Unknown said...

ah,, nice,

could this code be modified to work with illustrator(*.ai) files?

Adam Pletcher said...

@eugene: After Photoshop loads an AI file you can work with it just like any other image.

If you mean scripting inside Illustrator instead of Photoshop, then yes you can do that as well. Illustrator also has a scripting API, here's the Adobe documentation:
Illustrator Scripting

Similar to Photoshop, the VBScript documents are probably the closest match to the COM APIs you'd use from Python.

Nico Dufort said...

Eugene, while I don't use AI, a quick search brought this up--perhaps it could be of some use to you:


I think I would try the same approach as with PS.

Unknown said...

Thanks for the reply,

I need something similar to below for illustrator and wonder how you knew that Photoshop.ExportOptionsSaveForWeb exists.

options = win32com.client.Dispatch('Photoshop.ExportOptionsSaveForWeb')

Adam Pletcher said...

The ExportOptionsSaveForWeb method is listed in the Photoshop CS5 VBScript Reference document. For Illustrator CS5, the document you want is Illustrator CS5 VBScript Reference. If you're using a release earlier than CS5, grab the right one from the Illustrator Scripting page I listed above.

While those docs are for VBScript, VB methods are essentially COM methods, and the Python techniques I showed in this post should work. You just need to use the Illustrator-specific methods listed in its scripting API doc.

Unknown said...

ah thanks.

I've looked at their api and
options = win32com.client.Dispatch('Illustrator.ExportOptionsPNG24')

is what I need and I need to set
artBoardClipping to true so that image size doesn't get cropped.

but it seems win32com doesn't seem to support this option changed.
I'd like to look at the python source code so that maybe I can add the option. any rough guess where I should look?

options2 = win32com.client.gencache.EnsureDispatch('Illustrator.ExportOptionsPNG24')
>>> dir(options2)
['CLSID', 'SetMatteColor', 'SetObjectValue', '_ApplyTypes_', '__call__', '__doc__', '__eq__', '__getattr__', '__init__', '__int__', '__module__', '__ne__', '__repr__', '__setattr__', '__str__', '__unicode__', '_get_good_object_', '_get_good_single_object_', '_oleobj_', '_prop_map_get_', '_prop_map_put_', 'coclass_clsid']

Unknown said...


There artBoardClipping is listed but not listed when dir()

Unknown said...

almost found..
under $PythonHome/Lib/site-packes/win32com/gen_py/A3A65...../ExportOptionsPNG24.py

just need to modify the source code and find out how to apply (maybe need convert it to *.pyc file?) so that I can use the modified version

Adam Pletcher said...

I doubt that approach will work. COM methods are defined by the application itself (Illustrator), not by a Python file sitting somewhere. The Win32 Python extensions just offer a way to "late-bind" to those methods from Python. Which is also why dir() does not show them.

I would recommend asking how to use specific methods on Adobe's Illustrator Scripting Forum.

Unknown said...


Actually, it did work.

def SetArtBoardClipping(self, arg0=defaultUnnamedArg):
return self._oleobj_.InvokeTypes(1883324995, LCID, 8, (24, 0), ((11, 0),),arg0

is the code that I inserted into the file.

Exactly, COM APIs are defined by illustrator, and python didn't have function to call it, and I just inserted a function.

I'm stuck with another problem though, illustrator has something called objects(opposed to layers, and I didn't know about it until today). I need to convert them to layers programmatically.

Looks like pywin32 lacks many apis that are available to officially supported photoshop/illustrator script language.

Although, python can do most of what I need with illustrator, looks like i'll need some other script's help to complete the task :(

Adam Pletcher said...

Updated a couple Adobe links that had gone dead.

xxvii-xxiv-xiv said...

Saved my day. Thank you :).

Chris V said...

Great info, thanks!...but I have a question: How do you access the python script from within PS? I read that one could create a simple javascript to call the python script, but I'm not sure how it's done, I know little about javascript. I've tried
System.Diagnostics.Process.Start("python.exe", "C:\\psTest.py");
but it doesn't seem to do anything.
Also, is there a way to create a custom menu in PS where I can place my scripts? I do this in Maya for our company tools, and ideally I'd like to maintain a similar setup in PS.

Nico Dufort said...

I have not done any of this for years, but if I remember properly, I had a setup of Action Buttons in Photoshop, running a simple Javascript command to execute the Python code:

// pyCheckOut.jsx
var checkout = new File("/sandbox/photoshop/pyCheckOut.py");

I used to keep both files (.py, .jsx) in the same directory for the sake of keeping it organized.
Not sure if that helps. Maybe things are easier nowadays?

Also, when done debugging, use .pyw to prevent consoles from popping up all over the place.

Let me know if this helps.

Chris V said...

Nico, thanks for the help, that was almost too easy. Knowledge is king! Thanks again.

Francis Vega said...

Thanks nico for the js tip :D
How I could pass params?, anything like:

var checkout = new File("/sandbox/photoshop/pyCheckOut.py 1024 768 AnyName");


Nico Dufort said...

I don't have access to a Windows machine or Photoshop, but my first test would be to print out the arguments to see if your script picks them up (mainly to check the index of each in sys.argv[])

Daniel Serodio said...
This comment has been removed by the author.
Daniel Serodio said...

Great post, it really helped me getting started with PyCOM Photoshop scripting.

How can I access Photoshop constants like Units and TypeUnits? I tried win32com.client.constants.Units but I get an AttributeError...


Nico Dufort said...

Try TypeUnits instead. Pages 130, 172 of the reference guide will tell you how to use it (again, sorry, I don't have access to Photoshop):


Captain Denis Super Awesome said...

All this has worked amazingly for me.. except for some damn reason the application.documents object doesn't respond to any of it's documented methods or properties except for .parent (and although not documented the .count) Anyone else run into this?

Chris V said...

I'm having the same selection problem that Nico solved using comtypes, is there a fix for win32com yet? And if not, can someone explain the comtypes solution? Is it a replacement for win32com?

当当 said...

this is a life saver post, thank you guys. But I get a question, when I look into the documentation of photoshop_cs5_vbscript_ref.pdf I found there's only ExportDocument function there but no Export function.

How do you guys figured it out this ?

Adam Pletcher said...

That is either a typo in the VBScript documentation, or perhaps a difference between the VBScript and COM interfaces.

The "Export" function on document objects is still there in Photoshop CS5 and callable from COM/Python, and there is no function named "ExportDocument".

Jay Goodman said...

Wow, really useful. Thank you for the post!

Jo said...

Hi, I'm not sure if anyone reads this post anymore but I have a question regarding python interfaces in photoshop.

My problem is that it seems to slow down Photoshop a lot. I have a simple interface generated in Flash that call some jsx script themselves calling a PyQt script using com to interact with Photoshop. I'm not entirely sure it is the simplest way to go, but it does the trick. Only thing is, for some users, it slows down the Photoshop interface.
Any idea where it might come from ? Any suggestion would be appreciated ! Thanks

Adam Pletcher said...

I still read this! :)
I don't have experience with that exact kind of setup (JSX->Py->COM->PS), but...

If you have that kind of circular connection in the tools, I'd suspect it's waiting around for the last "message" to be processed, which is starving the UI and making it unresponsive. Hard to say without seeing the exact code, however.

I would suggest adding some timing code and profiling it, see where the time is going.

Nico Dufort said...

From memory, I can say that any code in Python I had back then was slower than the same code in Javascript (I had no external GUI, just Action buttons).

What Adam suggested might be a good place to start, but unless you have a strong 'fu', I have a feeling that regardless of how much you optimize your code, it will still be slower.

Jo said...

Thanks for the reply :)

Well the exact code is a lot to read.. I actually didn't write it, so I have to dig in it before I can get out some simplified code.

But I'll try the timer thing, see what it gives. Thanks for the tip !

Jo said...

I didn't see what Nico said.
Well actually, it's not really my code that is the problem. The real issue, is that it slows down the actions in Photoshop itself. For example, cancelling an action will not be immediate. Or switching from one layer to another will take 1 second instead of being instantaneous.

Evan said...

I had been wondering about this for a while and since this helped me so much I figured I would share the answer no matter how basic it may be.

When I tried to make all this work I had some troubles. Not only did I have multiple versions of photoshop but I had multiple bit versions (CS5 x86, CS6 x86/x64, CC 2014. I knew I had to use win32.client.Dispatch(Adobe.Application) to connect to the app and open a document but I could not connect to the Adobe.ActionReference or Adobe.ActionDescriptor

I tried to find out why my Application returned a timeout error and it seems it revolved around the Generic call to the Photoshop COM. I needed to specify the application version...but what was the number?

After much searching I found in the Windows Registry all Photoshop call actions and Versions to use.

If you want to make an Action Manager interaction without photoshop timeing out specify the version

Photoshop Application COMM Dispatch Commands Action Manager
(will use the recent version of photoshop installed)


For a full list of commands specific to you go to Start->(Search)regedit -> Locate Photoshop.X registry details

Rustomji said...

Evan, your post was enormously helpful, thank you!

Anonymous said...

amazing post, thank you a billion !!

The Smart Digital said...
This comment has been removed by a blog administrator.
Anonymous said...

It's 10 years later since the tutorial has been written and there's not much more sources to look for knowledge about this topic ;) Thanks a lot, finally managed to understand the SaveAs options objects.

Anonymous said...

how to run .ATN file using phthon?

suresh said...
This comment has been removed by the author.
Best Python Tutorials said...

excellent tutorial I am searching for long days python Tutorials


Greetings !

We are brisklogic (www.brisklogic.co).
Dynamo's of digitization” with a remarkable heritage.
Build digital products faster, smarter, and scalable.
Brisklogic is Solutions for a small digital planet.
Develop your business faster with less efforts with Us.
Let’s Make Things Better.

Sr. Business Manager
Thanks & Regards,