Paolo Amoroso's Journal


I shut down Free Python Books, the list of Python books that are free to read online or download I had been maintaining since 2019.

I archived the project's GitHub repo, which is now read-only and no longer accepts contributions. If you're interested you're welcome to fork the repo and maintain your copy.

When learning Python I came across many great free Python books, so I started keeping track of them. In early 2019 I shared my list on Reddit where it resonated with many. I later published the list on GitHub and it eventually gained over 4,500 stars, about 600 forks, and over 100 watchers.

In 2023 I rediscovered my love of Lisp and lost interest in Python, which I no longer use. Hence my decision to mothball the project.

Thanks to all who expressed appreciation or contributed, and to the Python community for producing so many great works.

#Python #books

Discuss... Email | Reply

I cancelled my annual Replit Hacker plan and I'll let it lapse at the end of December of 2023.

Replit is a popular and growing multi language development environment in the cloud. I've been subscribing to the Hacker plan for the past few years and it worked well for my Python projects, as such an online environment is a good match for my chromeOS lifestyle. But two changes made me reconsider the value of the product.

The first is Replit changed its paid tiers by raising the price of my plan and removing some features. I actually saw it coming when Replit began receiving massive funding by major investors, who are likely pressuring the company to reduce costs and turn profits. Still, this left a sour taste as it felt like a bait and switch.

The other change is my shift of focus back to Lisp, the programming language I love most and know best.

Replit doesn't work well with a highly interactive language like Lisp. It doesn't directly support any Lisp dialects and its default code editor provides little or no integration with a running Lisp image. Moreover, the files created or uploaded outside of the editor are often not preserved across sessions. If I have to install and maintain a full Lisp system in the Linux shell of a Replit workspace, I might as well install it locally on my desktop computer.

Finally, like other development tool vendors, Replit is doubling down on AI coding features which affect the cost of paid plans. But I'm not interested in this as the whole point of my hobby programming is to write all the code myself and learn from the experience.


This post was shared on Hacker News and I blogged about the reactions.

#development #Python #Lisp

Discuss... Email | Reply

Planet Python carried out my request to remove my blog from the aggregator. Now their feed no longer syndicates my posts about Python, which I'll no longer write much about.

Planet Python is an aggregator of blogs, podcasts, and other resources of interest to the Python community. In late 2019 I submitted the feed of Python posts of my old blog, later updating it to point to my new blog.

I was learning the language and sharing on the blog my experience with coding projects and other experiences. But although I had great fun with Python and accomplished a lot I'm proud of, my interest waned as I rediscovered my old love Lisp.

I encountered Scheme in the early 1990s at an introductory computer science class based on Structure and Interpretation of Computer Programs, fell in love with the Lisp family of languages, and learned Common Lisp and Emacs Lisp. Lisp became my only language until the early 2010s when real life claimed my time and attention. Near the end of the decade, intrigued by Python and its massive ecosystem, I decided to learn it.

At the beginning of 2023 I discovered Medley Interlisp and got hooked.

Using Interlisp and its environment made me realize Lisp is the language that comes most natural to me, I'm most productive with, and gives me joy and not just fun. I never mastered and enjoyed other languages to the level of Lisp. And my projects turned out not to need Python's batteries.

I'll still maintain a reading knowledge of Python and keep up with its ecosystem. But this journey made me readjust my focus on Lisp, now my only language.

It's good to be back home.

#Interlisp #Python #Lisp #blogging

Discuss... Email | Reply

The iLoad feature of the Z80-MBC2 homebrew Z80 computer allows uploading binary code that runs on the bare metal.

I thought it would be fun to try iLoad with some Intel 8080 code generated by the asm80 assembler of Suite8080, the suite of 8080 Assembly cross-development tools I'm writing in Python. But the code crashed the Z80-MBC2, uncovering a major asm80 bug.

It all started when, to practice the toolchain and process, I started with a Z80 Assembly demo that comes with the Z80-MBC2, which prints a message to the console and blinks one of the devices's LEDs. I assembled the program with the zasm Z80 and 8080 assembler, uploaded it via iLoad, and successfully ran it.

Next, I ported the blinking LED demo from Z80 to 8080 code and assembled it with asm80. But when I ran the demo on the Z80-MBC2, it crashed the device.

The baffling crash left me stuck for threee months, as I had no tools for debugging on the bare metal and there were only a few vague clues.

I carefully studied the less than a hundred lines of code and they looked fine. To isolate the issue I cut the code in half, leaving the part that prints a message to the console, and transforming the blinking demo into this bare.asm hello world for the bare metal:

OPCODE_PORT     equ     01h
EXEC_WPORT      equ     00h
TX_OPCODE       equ     01h
EOS             equ     00h
CR              equ     0dh
LF              equ     0ah

                org     0h

                jmp     start

                ds      16

start:          lxi     sp, start
                lxi     h, message
                call    puts


message:        db      CR, LF, 'Greetings from the bare metal', CR, LF, EOS

puts:           push    psw
                push    h
puts_loop:      mov     a, m
                cpi     EOS
                jz      puts_end
                call    putc
                inx     h
                jmp     puts_loop
puts_end:       pop     h
                pop     psw

putc:           push    psw
                mvi     a, TX_OPCODE
                out     OPCODE_PORT
                pop     psw
                out     EXEC_WPORT


The constants at the beginning define the addresses of the output ports, the opcode for sending a character over the serial line, and a couple of control characters. Next, the program sets up the stack and iterates over the output string to print every character.

The simplified demo program still crashed the Z80-MBC2, forcing me back to the drawing board.

Then I had an epiphany. What if the binary code asm80 generates is different from zasm's?

I fired up the dis80 disassembler of Suite8080 and compared the output of the assemblers. Sure enough, the difference jumped at me: the destination addresses of all the branches after the label message are off by 5 bytes.

The instructions branch to addresses 5 bytes lower, so the call to puts executes random string data that chrashes the device. The last correct address asm80 outputs is that of the label message. The address of the next one, puts, is wrong and leads to the crash.

Indeed, the same demo code assembled with zasm ran fine on the Z80-MBC2 and printed the expected message. This confirmed my hunch.

What now?

The next step is to find the bug in the Python source of asm80, which I'm developing with Replit. Although Replit provides a debugger, I won't use it. The tool is not well documented and I'm not sure how it works. In addition the Replit debugger is best suited to code started from the run button. This is inconvenient for command line programs like the Python scripts of Suite8080.

Therefore, I'll take the opportunity to use Python's native debugger pdb, which I always wanted to try in a real project. I played with pdb a bit and it looks easy to use, with all the commands and options handy.

Let's see if pdb can help me pinpoint the bug in the Python code.

#Suite8080 #z80mbc2 #Assembly #Python

Discuss... Email | Reply

If you lived through the personal computer revolution of the 1980s, you may have read some books that got you hooked with programming. These works led the reader through the intellectual adventure of using computing to explore interesting problem domains.

Two recent Python books bring back this fascination and excitement with programming, Impractical Python Projects: Playful Programming Activities to Make You Smarter and the sequel Real-World Python: A Hacker's Guide to Solving Problems with Code, both by Lee Vaughan and published by No Starch Press.

They are not Python tutorials or guides. Instead, they present stimulating coding projects for non-programmers who want to use Python for doing experiments, test theories, or simulate natural phenomena. This includes professionals who are not software developers but use programming to solve problems in science and engineering. And, of course, hobbyists.

Exploring and understanding the problem domain is an integral part of the books' projects along with coding. This is unlike typical programming books where the examples are often trivial, have little or no domain depth, and are stripped of everything but the essentials.

The science and engineering Vaughan's books cover include some great projects that match my interest in astronomy and space. For example, Impractical Python Projects has chapters on estimating alien civilizations, simulating a volcano on Jupiter’s moon Io, simulating orbital maneuvers, and stacking planetary images. Real-World Python discusses re-discovering Pluto, plotting the Apollo 8 lunar trajectory, selecting martian landing sites, and detecting exoplanets.

The sample code is straightforward, clear, and hints at how much can be done with little code. Since the books are not language tutorials, they focus on prototyping and exploration rather than building large and maintainable systems.

#Python #books

Discuss... Email | Reply

First reading about Flet made me jump over my chair as it's what I was long looking for, a solution to my web and Android development needs. Flet is an opinionated, Flutter-based GUI framework for creating multi-user web, desktop, and mobile applications.

What I was looking for is an easy way of creating simple web and Android apps in Python. Web frameworks such as Django are overkill and too low level for me, and in most cases require JavaScript or other non-Python frontend code.

As for mobile, although there are Python frameworks for Android development like Kiwi and BeeWare, they come with the ballast of a heavy Java and Android SDK toolchain.

Flet overcomes these issues. It enables creating web apps that hide a web framework under the hood. And, without touching Java, Flet can make also PWAs that run on Android and other mobile platforms. All from the same fully Python code base. Plus, deploying Flet web apps to my favorite Python environment, Replit, is well supported and straightforward.

I'm closely following the development of Flet and will experiment with the framework.

#Android #Python

Discuss... Email | Reply

I decided what to work on next on Suite8080, the suite of Intel 8080 Assembly cross-development tools I'm writing in Python. I'll add two features, the ability for the assembler to trim trailing uninitialized data and a macro assembler script.

Trimming uninitialized data

Consider this 8080 Assembly code, which declares a 1024 bytes uninitialized data area at the end of the program:

# . . .

data:        ds    1024

For this ds directive, the Suite8080 assembler asm80 emits a sequence of 1024 null bytes at the end of the binary program. The executable file is thus longer and may be slower to load on the host system, typically CP/M.

The Digital Research CP/M assemblers, ASM.COM and MAC.COM, strip such trailing uninitialized data from binaries. After asking for feedback to r/asm, I decided to do the same with asm80. I should be able to implement this optimization by adding just one line of Python, so the feature is a low-hanging fruit.

Macro assembler

asm80 can accept source files from standard input, which makes it possible to combine the assembler with an external macro preprocessor to get a macro assembler. Thanks to its ubiquity, M4 is the clear choice for a preprocessor.

Assuming prog.asm is an 8080 Assembly source file containing M4 macros, this shell pipe can assemble it with asm80:

$ cat prog.asm | m4 | asm80 - -o

The - option accepts input from standard input and -o sets the file name of the output binary program.

The other Suite8080 feature I'm going to implement is a mac80 helper script in Python to wrap such a shell pipe and make assembling macro files more convenient. In other words, syntactic sugar wrapping asm80 and M4.

The script will use the Python subprocess module to set up the pipe, feed the proprocessed source to the assembler, and not much else.

#Suite8080 #Python

Discuss... Email | Reply

The Python feed of my old blog Moonshots Beyond the Cloud has long been aggregated by Planet Python. But I'm no longer going to update that blog, so I removed the old feed from Planet Python and submitted the Python feed of my new blog, Paolo Amoroso's Journal.

#Python #blogging

Discuss... Email | Reply

It all started when I added a new Intel 8080 Assembly demo to Suite8080. Pushing the commit to the GitHub repo triggered a rebuild of the project documentation hosted on Read The Docs, which failed.

I maintain the Suite8080 documentation with Jupyter Book and publish it to Read The Docs with Sphinx as the backend.

Over the previous months, while my work on Suite8080 was on hold, some backward incompatible Jupyter Book update broke the Suite8080 documentation configuration. I had no idea what to do, so I opened a Read The Docs issue. After some troubleshooting with the help of Manuel Kaufmann, Benjamin Balder Bach contributed a Suite8080 pull request that fixed the issue.

Benjamin's patch has an additional advantage. I no longer have to manually edit to let sphinx.ext.autodoc discover the project's Python packages.

#Suite8080 #Python

Discuss... Email | Reply

The GitHub repo of my Free Python Books project is about to get 3.5K stars. As I write this it has 3,499 stars, 448 forks, and 149 watches.

This milestone is mind blowing, humbling, and unexpected.

The project is a categorized list of Python books that are free to read or download. Despite the simplicity, something in the resource resonates with many Python developers and enthusiasts.

It all started as a personal list of books I discovered while learning Python, which I wanted to read later or reference. I shared the early list on Reddit and it snowballed from there.

I'm happy also because this success hints there are many learners who, in the age of video, still seek books and text resources.

#Python #books

Discuss... Email | Reply