Package: toyboxpy
toybox.pyALua,Cand asset dependency manager for thePlaydateSDK.toybox.pyis a Python port ofJeremy McAnally's toybox app.toybox.pylets you easily use, create and share third party libraries, calledtoyboxes, for anyPlaydateproject. It handles all dependencies betweentoyboxesautomatically and provides precise versioning for eachtoybox.Sometoyboxesmay provideCcode, some may provideLuacode orLuaextensions written inCand some may provide just assets. Sometoyboxesmay provide all three or only two of these, it's completely up to thetoyboxcreator and maintainer.The main repo for the project is oncodeberg.Playdate is a registered trademark ofPanic.Table of ContentsInstallationUsageUsing Lua toyboxesUsing C toyboxesCreating your own toyboxesLicenseInstallationtoybox.pyis a pure Python project. It requires at leastPython3.7 and access to thegitcommand line tool. Make sure you have asupported versionofPythonbefore proceeding.You can installtoybox.pyby typing the following in a terminal window:pip install toyboxpyUsagetoybox.pysupports various commands, sometimes with one or two extra arguments:toybox <options> <command> <arguments>The following commands are supported:help <topic>             - Show a help message. topic is optional (use 'help topics' for a list).version                  - Get the current version.license                  - Show the license for the app.info                     - List your current dependencies.add <name/url> <version> - Add a new dependency (version is optional).remove <name/url>        - Remove a dependency.update <name/url>        - Update a dependency or all dependencies if no argument is provided.check                    - Check for updated toyboxes.store <subcommand>       - Get info about the toystore (use \'help store\' for more info).set <name> <value>       - Set a configuration value for this toybox.setupMakefile            - Setup a basic makefile project for using C toyboxes.The following options are supported:--help/-h                - Show a help message.--force/-f               - Forces the execution of some commands, even if things seem up to date.--local/-l <folder>      - Update the dependencies as local, if found in this folder.--debug                  - Enable extra debugging information.toybox.pyshould always be run from your project's root folder. Although it doesn't use andgitcommands directly on your project folder, somegitcommands thattoybox.pyuses do require that your project folder already be agitrepo. A simplegit initsuffices. This also makes it easy for you to back-track any unwanted changes.toybox.pycreates and uses a local file at the root of your project's folder namedBoxfile. You can use theaddandremovecommands to modify it.Adding dependenciesTheaddcommand takes up to two arguments:toybox add <name/url> <version>The first argument should either be name of a toybox, if it is listed in thetoystore, the full url to agitrepository which contains thetoyboxyou would like to add, such ashttps://codeberg.org/DidierMalenfant/modplayer.git. For example, because `modplayer is on thetoystore, any of the short forms below will point to the same repository:toybox add modplayertoybox add https://codeberg.org/DidierMalenfant/modplayertoybox add codeberg.org/DidierMalenfant/modplayertoybox add DidierMalenfant/modplayertoyboxesdo not need to be hosted onGithubbut, as shown above, if the server url is omitted then Github is assumed.Adding a dependency with anameorurlwhich already exists for your project will replace theversionused for that dependency.Theversionparameter is optional and allows you to select a specific or a range of versions for thetoyboxsuch as1.4.12. If noversionargument is provided then the lastest version of the latest major version of thetoybox's repo, if available, is used.You can also require a specific minor version with1.4or a specific major version with1. In that case, the latest version with the given minor or major version numbers will be used which allows a developer using yourtoyboxto stay up to date with bug fixes or new features without risking an API change from breaking your project.If you would like to fine tune your version requirement even more, you can instead use up to two comparaison operators in theversionargument. For example'>1.2.3 <=3.0.0'. Keep in mind that, here too, validsemverversion numbers should be used. Major or minor versions can be still used in combination with comparaisons. For example, a version requirement like'3 <3.9.0'results in all versions higher or equal to3.0.0but less than3.9.0. Supported comparaison operators are>,<,>=,<=.You can also specify the name of a branch when, for example, using a development version of a giventoybox. In that case, the latest commit from that branch is used when updating.toybox add modplayer developThere may be times, like during development of atoybox, when you'll want to use a local version of atoyboxinstead of one found on a server. In order to do that, just replaceversionby the path to the local folder that contains yourtoyboxand then use theupdatecommand. This path need to start with either~or/. In order to prevent you from committing files that come from a localtoyboxby mistake, using localtoyboxeswill also create a pre-commit hook that will prevent you from making any commits while localtoyboxesare used.Note that you can do this temporarily by using the-loption together with theupdatecommand described below.OnmacOSandlinux, the content of a localtoyboxis be soft-linked inside your project instead of copied. This way, any modifications you make during development will modify the actual localtoyboxdirectly. OnWindows, the files from the localtoyboxare just copied over.In order to restore everything after using localtoyboxes, just add the regular version back and use theupdatecommand again.Removing dependenciesYou can remove atoyboxwith the remove command:toybox remove <name/url>Updating dependenciesOnce you've added or modified a dependency, you can update its content within your project by using theupdatecommand:toybox update <name/url>If nodependencyargument is used then all dependencies are updated.toyboxrecords the current version of any dependency installed in your project so keep in mind that this may modify theBoxfilefor your project.If you already have all your dependencies up to date thenupdatewill have no effect. If you need to force your dependencies to update, you can use the-foption.toybox -f update <name/url>If you have local versions oftoyboxesthat you want to use instead of the ones found at the URL provided in the Boxfile, for example when you are testing changes on thosetoyboxeslocally, you could modify eachtoyboxentry separately but you can also use the-loption:toybox -l <folder> updateThis will update all your dependencies and for any of those, if thetoyboxis found insidefolderthen this local version will be used instead. You can then simply use a regulartoybox updateto put everything back in place and use the online content of the repos again.Checking for updatesYou can check to see if any of thetoyboxesyou use have been updated by using thecheckcommand:toybox checkThis will not modify anything within your project, it will just let you know if anything new is available for you.Using Lua toyboxesAnytoyboxwill be installed in a subfolder namedtoyboxesat the root folder of your project. If any of thetoyboxesprovidesLuacode, a file namedtoyboxes.luawill be created in that folder. All you need to do to start using yourtoyboxesis import that file anywhere in your project.Assuming you are using the standardproject structuresuggested by thePlaydate SDKand have yourLuasource files in a subfolder namedsourcethen you can do this by adding this import statement in any file that uses thetoyboxes:import'../toyboxes/toyboxes.lua'Note that due to a bug in thepdcapp used by thePlaydate SDKto process source files, the.luaextension is required here. Once this bug is fixed, this will no longer be needed.Using C toyboxesAnytoyboxwill be installed in a subfolder namedtoyboxesat the root folder of your project. If any of thetoyboxesprovidesCcode, a file namedtoyboxes.mkand a file namedtoyboxes.hwill be created in that folder.Assuming your makefile is in your project's root folder, you will need to include this makefile in your own before you include thePlaydateSDK's common makefile:include toyboxes/toyboxes.mk...include $(SDK)/C_API/buildsupport/common.mkYou will then need to call thetoyboxesinit macroREGISTER_TOYBOXESduring thekEventInitLuaevent and pass it aPlaydateAPI*:#include"toyboxes.h"#include"pd_api.h"inteventHandler(PlaydateAPI*playdate,PDSystemEventevent,uint32_targ){if(event==kEventInitLua){REGISTER_TOYBOXES(playdate)}return0;}If you don't know how to or your project doesn't already useCextensions, you can use thesetupMakefilecommand to create a bare bones makefile project, like the one above, in the current folder. Remember that makefile projects need to be built withmakeand notpdc.Using assets from toyboxesIf you want, or need, to access atoybox's assets directly they will, if any, be located in a folder namedtoybox_assetsinside thesourcefolder of your project. The folder is organised bytoyboxURLs so, for example, an asset namedMyPicin atoyboxnamedMyRepofrom userUsernmameongithubwill be located atsource/toybox_assets/github.com/Username/MyRepo/MyPicUsuallytoyboxesshould provideLuamethods to access their assets so you shouldn't need to do this.Using luacheckIf any of thetoyboxesprovideluacheckconfiguration in asupported format, thentoybox.pywill generate a file namedluacheck.luainside thetoyboxesfolder. This file can be used to import thisluacheckconfiguration in your project's.luacheckrc, for example, like this:require"toyboxes/luacheck"(stds,files)std="lua54+playdate+toyboxes"operators={"+=","-=","*=","/="}Make sure to always runluacheckfrom the root folder of your project in order for these definitions to work correctly. For the time being, compound operators support is not part of the main branch of Luacheck. You can experiment with it by installing thisforked branchof Luacheck.The toystoreThetoystoreis an online registry of knowntoyboxesand theirurlso that they can be referred to by only their names. It is located onGithuband newtoyboxescan be added to it by simply submitting a PR. This is purely for convenience though and there is no obligation that atoyboxbe added to thetoystorefor it to be freely available to anyone since you can always use itsurldirectly.To see a list of all availabletoyboxesin thetoystoreyou can use thestore contentcommand:toybox store contentYou can also get more information about a given toybox or open a browser to see the repo for a toybox with thestore infoandstore repocommands:toybox store info pdbaseInformation from thetoystoreis cached locally on your computer. You can use the-foption to force re-downloading this information.Adding the toybox powered badgeIf your projects usetoyboxes, you can let others know that they areby adding this badge to yourREADME.mdfile:[![Toybox Powered](https://img.shields.io/badge/toybox.py-powered-orange)](https://codeberg.org/DidierMalenfant/toybox.py)Creating your own toyboxesOf course the best part oftoybox.pyis that anyone can create, distribute and maintain their owntoyboxesfor others to use. All you need it agitrepo (which can be located anywhere on the internet) and to make sure that some of your code is laid out in a way thattoyboxcan process and understand.For starters, the name of yourgitrepo will be the nametoyboxuses for a lot of things. It's better not to use the name of an existingtoyboxas this could cause clashes with othertoyboxes.toyboxescan provideLuamethods, either written inLuaor inCas extensions to theLualanguage, orCmethods that can be used by others when writing their ownCcode for thePlaydate. Yourtoyboxcan provide just one, two or all three of these types of extensions.Versionning fortoyboxesis done via tags in the git repo for yourtoybox. Those tags should be a validsemverversion and can optionally be prefixed by av. For examplev2.3.0is correct,v2.3is not. The most important part for users of yourtoyboxis to make sure that you only fix issues when incrementing a patch version, that you only add new functionality when incrementing a minor version and that you always increment the major version when adding things to yourtoyboxthat may break things for your users (removing deprecated methods or changing the API for example).It's usually a good idea, rather than provide a swiss-army knife type oftoybox, to try and make sure yourtoyboxesprovide just one service and do it well. Split different functionality into separatetoyboxesso developers can only add the ones they need.toyboxescan depend on othertoyboxes. All you need is to add aBoxfilein the root folder of yourtoyboxand it will be taken care of automatically when resolving dependencies. Be careful, during development for example, to not resolve that dependency directly in your project folder. You could end up committing the resultingtoyboxesfolder which would be redundant when others use yourtoybox. Instead you can use it as a localtoyboxin a test project.ALuatoyboxcan depend on aCtoyboxand aCtoyboxcan depend on anotherCtoybox. You don't even need any extra import statements because anyLuatoyboxesyourtoyboxdepends on should already be imported before yourtoyboxe's import file is imported.Try to make sure thattoyboxesdon't cross-depend on each other (ArequireBandBalso requiresA) as this is usually a sign of some API design issues and can complicate things in the long run.Creating a Lua toyboxCreating aLuatoyboxis as simple as adding oneLuafile at the root to your project's repo or in a subfolder namedsourceorSource. That file can be namedimport.luaor the same as your project and must contain all the import statements required to use yourtoybox. For example, the fictionalMyPdPitoybox, if written inLua, would contain one file namedMyPdPi.luawhich would look like this:----  MyPdPi - Calculate Pi to an infinite number of decimals.--import"math"import"picalc"import"utils"The source code itself in yourtoyboxcan be laid out any way you want (additionalSourcesubfolder, etc...) as long as this file imports all the other files correctly.If you wish to use a completely custom name or location for theLuafiletoyboxwill import for your project, you can use theset lua_importcommand:toybox set lua_import mycustomfile.luaThis will create aBoxfilein your project, if one didn't already exist, and will set a configuration parameter fortoyboxto use thatLuafile when importing yourtoyboxinstead of the default.Creating a C toyboxCreating aCtoyboxis almost as simple and requires three things. First you need to create a makefile for yourtoyboxin the root folder of your project and name it after your project. Once again, if the fictionalMyPdPitoyboxwas written inC, it would contain one file namedMyPdPi.mkwhich would look like this:##  MyPdPi - Calculate Pi to an infinite number of decimals.## -- Find out more about where this file is relative to the Makefile including it_RELATIVE_FILE_PATH:=$(lastword$(MAKEFILE_LIST))_RELATIVE_DIR:=$(subst/$(notdir$(_RELATIVE_FILE_PATH)),,$(_RELATIVE_FILE_PATH))# -- Add us as an include search folder only if it's not already thereuniq=$(if$1,$(firstword$1)$(calluniq,$(filter-out$(firstword$1),$1)))UINCDIR:=$(calluniq,$(UINCDIR)$(_RELATIVE_DIR))# -- Add our source files.SRC:=$(SRC)\$(_RELATIVE_DIR)/MyPdPi/MyPdPi.cThe first section is very important and makes sure that the makefile is relocatable, i.e. can work no matter where the includer's makefile is located. The second part adds the root folder of yourtoyboxas an include path. The last section adds the source files that need to be compiled for yourtoybox. Don't forget the$(SRC)on the first line to make sure that any previous sources files from othertoyboxesare included.The use of:=instead of=is also very important here as it forces make to resolve the value right here and there, instead of when it is used because it could have been overwritten by anothertoyboxat that point.If you wish to use a completely custom name or location for the makefiletoyboxwill import for your project, you can use theset makefilecommand:toybox set makefile subfolder/OtherMakefileThis will create aBoxfilein your project, if one didn't already exist, and will set a configuration parameter fortoyboxto use thatLuafile when importing yourtoyboxinstead of the default.Header files for your project should be located in a subfolder named after your project, in our caseMyPdPiand it should contain at least an include header named after your project, i.e.MyPdPi.hwhich, at a minimum, looks like this:/**  MyPdPi - Calculate Pi to an infinite number of decimals.*/#ifndef MYPDPI_H#define MYPDPI_H#include"pd_api.h"// -- GlobalsexternPlaydateAPI*pd;// -- toybox registration functionvoidregister_MyPdPi(PlaydateAPI*playdate);#endifIf you wish to use a completely custom name or location for the include filetoyboxwill import for your project, you can use theset include_headercommand:toybox set include_header Folder/OtherHeader.hThis will create aBoxfilein your project, if one didn't already exist, and will set a configuration parameter fortoyboxto use thatLuafile when importing yourtoyboxinstead of the default.As shown above, yourtoyboxheader file needs to declare at least one function and that's the function called during thekEventInitLuaevent. The name of that function needs to beregister_<toybox_name>and take aPlaydateAPI* playdateas an argument. If you're registering extensions to theLualanguage this is where that would take place.You can also declare any other functions yourtoyboxexposes or include any other header files that may be needed. If yourtoyboxis just providingCcode for other projects and doesn't require any particular initialisation you can leave this method empty or just grab thePlaydateAPI*for future use elsewhere in your code, like so:/**  MyPdPi - Calculate Pi to an infinite number of decimals.*/#include"MyPdPi/MyPdPi.h"// -- GlobalsPlaydateAPI*pd=NULL;// -- toybox registration functionvoidregister_MyPdPi(PlaydateAPI*playdate){pd=playdate;}Providing assets in your toyboxIf yourtoyboxuses or provides assets, they should be located in a folder namedassetsat the root of yourtoyboxfolder. This folder will be moved into the end-user'ssourcefolder during installation so that it can be accessible bypdcduring compilation of the project. You will therefore need to use a specific path in order to reach those assets from your code.For example, if ourMyPdPitoyboxcontained an image namedMyPic.pnginassets/imagesand was available via agithubrepo namedMyRepofrom userMyUsernamethen accessing the asset can be done as follows:image = playdate.graphics.image.new(`toybox_assets/github.com/MyUsername/MyRepo/images/MyPic`)While this works when the code is internal to yourtoybox, when you need to provide assets to the end-user of yourtoybox, it is much more elegant to provide utility methods in order to access your assets, like this:function MyPdPi.getMyPic()
    return playdate.graphics.image.new(`toybox_assets/github.com/MyUsername/MyRepo/images/MyPic`)
endThat way they do not have to deal with the path to your asset. Check out theFontSamplesampletoyboxfor an examples of how to do this.You can override the subfolder used for yourtoybox's assets by using theset assets_sub_foldercommand:toybox set assets_sub_folder My/Custom/PathThis will create aBoxfilein your project, if one didn't already exist, and will set a configuration parameter fortoyboxto use that subfolder inside thetoybox_assetsfolder. This isnot recommendedto use for productiontoyboxesas is could cause name collisions. It can still be useful if you forked atoyboxrepo for development but want to still keep the assets' path the same as the original repo.Adding luacheck globalsYou can provideluacheckconfiguration info in yourtoyboxthat others using it can automatically benefit from. It needs to be in a supported format fortoybox.pyto detect and use it.Just create a file namedLuacheck.luaand place it in a folder named 'luacheck' in your project's root folder. This file will contain list ofglobalsorread_globalsprovided by yourtoybox:-- Globals provided by MyPdPi.return{globals={MyPdPi={fields={super={fields={className={},init={}}},className={},init={},calculatePi={}}}}}The format of this file follows similar rules toluacheck's.luacheckrcconfig file.You can also still use this file for yourtoybox's itself by creating a.luacheckrclike this one in yourtoybox:stds.MyPdPi=require"luacheck/Luacheck"std="lua54+playdate+MyPdPi"operators={"+=","-=","*=","/="}Adding your toybox to the toystoreIf you want to make it easy for others to use yourtoybox, you can submit a PR to thetoystorein order for them to be able to refer to yourtoyboxsimply by its name. Entries in the toystore are located in the filetoystore.tomland are be in the following format:[Plupdate]url="https://codeberg.org/DidierMalenfant/Plupdate"description="The playdate.update() manager."author="Didier Malenfant <coding@malenfant.net>"license="MIT"Make sure that your entry is added in alphabetical order in the file and that the name you decided to use is unique, not too generic and descriptive.Letting others know about your toyboxIt's not required, but it's always a good idea, to add a word abouttoybox.pyin the README.md of yourtoyboxrepo so developers know what it contains and how to use it:**MyPdPi** is a [**Playdate**](https://play.date) **toybox** which lets you calculate Pi to an infinite number of decimals.You can add it to your **Playdate** project by installing [**toybox.py**](https://codeberg.org/DidierMalenfant/toybox.py), going to your project folder in a Terminal window and typing:toybox add MyGitHubUsername/MyPdPitoybox updateThis **toybox** contains both **Lua** and **C** toys for you to play with.You can also add a nicebadge like this:[![Toybox Compatible](https://img.shields.io/badge/toybox.py-compatible-brightgreen)](https://codeberg.org/DidierMalenfant/toybox.py)Don't forget to also let us know about yourtoyboxesvia social media (#toyboxpy, mentions or DM) so that we can spread the word too...Licensetoybox.pyis distributed under the terms of theMITlicense.