Saturday, 5 November 2022

Python Identity and Equality

There are quite a few things we should be aware regarding identity, equality and comparison in Python. I won't explain here the difference between identity (also known as reference equality, mainly in C#) and equality (also known, again mainly in C#, as value equality), it's a general Computer Sciences concept that you should already have clear before reading this post. I already talked about it quite a while ago.

In Python, For identity comparison we use the is and is not operators. They check if 2 variables point to the same object, so they hold the same memory location. ob1 is ob2 is the same as doing id(ob1) == id(ob2).

For equality comparison we use the == and != operators. The behaviour of == and != is determined by the __eq__ and __ne__ methods in the class of our object, so we can customize their behaviour (operator overloading) implementing them in our class. Otherwise, the attribute resolution algorithm we'll end up using object.__eq__ and object.__ne__. These default methods just do an identity/reference comparison. Obviously classes like str or int implement __eq__ and __ne__. This means that str.__eq__ is object.__eq__ is False. You can read more here

Because all types are (direct or indirect) subtypes of object, they inherit the default comparison behavior from object. Types can customize their comparison behavior by implementing rich comparison methods like __lt__(), described in Basic customization.

The default behavior for equality comparison (== and !=) is based on the identity of the objects. Hence, equality comparison of instances with the same identity results in equality, and equality comparison of instances with different identities results in inequality. A motivation for this default behavior is the desire that all objects should be reflexive (i.e. x is y implies x == y).

I've just talked about implementing the __ne__ method. Well, that's not necessary really, we only need to implement __eq__ to customize the equality behaviour for one class. != will try to find a __ne__ method in our instance, and as we have not implemented it it will end up in object.__ne__, that will call __eq__ in that instance (the method that we have implemented) and negate its result. You can find an in depth discussion here.

Python 3.x: indeed, the default implementation of __ne__ calls __eq__ and negates the result (but on the technical level there are some subtleties, please read on).

Hopefully, contrary to JavaScript, we could say that Python does not have type coercion/implicit conversion. I mean, the equality comparison implementations (__eq__, __ne__) in classes like int and str (or the __add__ method) will not automatically convert one type to another if it receives an object of a different type. In Python 5 == "5" is False, while in JavaScript it's True. In the same, way 3 + "5" in Python throws and Exception, while JavaScript converts the integer to string and returns "35".

in operator. The in operator is really useful and expressive. One should wonder how it works regarding value or reference equality. Well, answer gives such an excellent explanation. In short, "in" uses the __contains__ method defined in the container object. For standard containers __contains__ does this:

For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression x in y is equivalent to any(x is e or x == e for e in y).

Additional considerations. When comparing strings or numbers, we should always use "==" rather than "is". When checking if an object is None, and given that None is a singleton, in most cases using is or == would be equivalent, and is is considered better style. There could be some classes for which __eq__ has been implemented to make comparing to None as True, which I think is very unlikely. In those odd cases you should use ==, unless that you want to skip that odd behaviour...

Sort comparisons. Regarding the >, <, >=, <= comparisons, explains it pretty well.

Some polygloth notes: In Java we use the == operator for identity, and Object.equals (that we can override) for equality. In javascript it's commont to say that we have an identity operator === and an equality operator ==. As there is no operator overloading we can not customize equality ourselves, it's all determined by the algorithms defined by the language, which are very well explained here.

Sunday, 16 October 2022

Les Papillons Noirs

I've recently watched online on Arte.tv the amazing series Les Papillons Noirs (Black Butterflies), that I think is available on Netflix. I had seen some advertising on the street and I thought that it was set in the 70's, which was not particularlly appealing to me. Anyway, when I found that it would be available for free in Arte France for one month I decided to give it a try. I got hooked since the first minute. The 2-3 first episodes are really good. It's set in our days, telling a story that starts in the 60's. Albert (masterly played by Niels Arestrup), an old man with just a few months ahead, hires a writer (Modi, a hard guy that after a stay in prison rebuilt himself through writing) to write his biography. Modi (Nicolas Duvauchelle, that I knew from his pretty good performances in Braqo) is in a creative crisis and ends up accepting what apparently should be a boring, mundane task that will get him some cash. It won't be. It's not only Albert's story what is told, but also that of Solance, the love of his life. Two depossesed kids that grow together into two survivors that will end up changing sides, from that of the beaten ones to that of those who beat, and kill, and enjoy with it. The 70's, France, a rich, powerful country (las 30 gloriosas) a working class that can enjoy summer vacations, partying, sexual freedom. A free and optimistic country (so far away from these days), but where things could also go totally wrong.

The recreation of the 70's is amazing, the colorful aesthetic full of life and energy that contrasts with the dark aesthetic of the present, where Modi, his mother and Albert live in shadows. There's this so well chosen female character that makes her appearance in the middle of the series. A tattoo artist whose main role in the story seems at first to further enrich this visual journey by driving us into contemporary, alternative, cool, arty, sensual territories, and that will end up being a pivotal piece of this masterful story.

The characters grow and grow as the story evolves, so the last episodes are really tense, gripping, unsettling, thrilling... and then you have this twist in the story that left me in shock. A twist that I would almost compare to the one in Incendies (so if you've read my review of that Quebecoise masterpiece you get an idea of how much that means coming from me). When the last episode ended I was left with contradictory feelings. On one hand a profound satisfaction as I felt lucky to have spent my time with such an amazing story. On the other hand, the bad feeling left by the realistic question to this answer: how long will it take until I come across another story so good as this one? A long time I'm afraid.

Monday, 3 October 2022

Using async-await in Python to avoid Stack Overflows

Last year I wrote a post explaining how we can avoid stack overflows in javascript deep recursive functions by making the function async and placing an "await 'nothing';" before the recursive call (just read that post if you want a reminder). That works fine in JavaScript because you can await anything, not just a Promise (well indeed it will be converted in a Promise.resolve("anything")). Awaiting that already resolved promise will anyway suspend the current function, putting in the microtask queue the "continuation" function for that promise, that is, the call that will resume that function in the point after the await.

Things are a bit different in Python asyncio. You now that we have several types of awaitables (coroutines, Tasks, Futures...). In this post, when I say coroutines I'm referring to native coroutines, the ones that you define with the async keyword. I'm not talking about the coroutines that you could define in older versions of python by means of some decorator (honestly I have never used them). Native Coroutines return a Coroutine object, and its code will not run until you await for it. Additionally, they don't return a Future, they just get suspended/resumed in their await calls, and end up returning a value, but not as the result attribute of a Future created by the runtime. This is pretty different from JavaScript async functions, that always create a Promise that gets resolved to the value returned by the functions after its different suspensions/resumes. This is pretty important because a Python coroutine will suspend if the last coroutine in the stack of coroutine calls explicitely yields the control to the event loop. Coroutines are implemented as generators, so this "yield control" means yielding a Future or just None. When the future reaches the event-loop it will set a callback in the future (via add_done_callback) to resume the coroutine. This answer explains it nicely:

This part is a common misconception. Python's await doesn't mean "yield control to the event loop", it means "start executing the awaitable, allowing it to suspend us along with it". So yes, if the awaited object chooses to suspend, the current coroutine will suspend as well, and so will the coroutine that awaits it and so on, all the way to the event loop. But if the awaited object doesn't choose to suspend, as is the case with aprint, neither will the coroutine that awaits it.

There are several deep-dive articles that have helped me understand (maybe not fully, but at least to a "that's enough level") how coroutines are implemented by means of generators, yield-from, tasks and futures. This article is probably the most complete thing you'll find around. And this and this are also a good read. Bear in mind that as far as I've understood, for a chain of coroutine calls to run you will always have a Task that is controlling it, either directly created by you invoking asyncio.create_task, or created by the event-loop itself. When I mentioned above that coroutines yield futures to the eventloop, well, indeed the Future reaches the Task (that invoked the coroutine with task._step -> coroutine.send), so it's the Task who adds a callback to the Future. From the tenthousandmeters article:

What does task.__step() do with a future? It calls future.add_done_callback() to add to the future a callback that reschedules task.__step(). If the result is already available, the callback is invoked immediately. Otherwise, it's invoked when someone/something sets the result by calling future.set_result().

Being aware of those differences between Python and JavaScript, I thought I would not have problems to implement the "prevent stackoverflow" trick in Python as I did in JavaScript. Basically, instead of a simple "await 'whatever'" I would have to do an await asyncio.sleep(0)" that is the official way to return control to the eventloop. That way the current function gets suspended, meaning that we temporarily exit it, and when resuming it, I was assuming that the stack would be clean, meaning that the stack would not keep growing to an overflow. I was wrong. Given a simple recursive function:


def build_message(msg, counter):
    """
    Normal recursion. Will overflow the stack at around iteration 1000
    """
    if counter == 0:
        return msg
    # print(f"{counter}")
    msg += "a"
    return build_message(msg, counter-1)

If I write it in an async way like this, and invoke it with counter = 1000, it fails:


async def async_build_message_KO(msg, counter):
    """
    Try to prevent the stackoverflow by making it a coroutine and suspending it via asyncio.sleep
    It does not work
       """
    if counter == 0:
        return msg
    sleeper = asyncio.sleep(0.001)
    print(f"{msg[0]} - {counter}")
    # print(f"{counter} - {type(sleeper).__name__}")
    await sleeper

    msg += "a"
    return await async_build_message_KO(msg, counter-1)

The above code causes a stackoverflow once the stack reaches the 1000 items limit. I tried sleep(0.001) (not just with sleep(0)) to make sure that we are really suspending the execution for a (minimum) amount of time. I was quite confused, and googling around I found a solution, invoking the recursion via create_task. With the above I can invoke it with counter=5000 and it will work nicely. Notice that it works perfectly fine both if using tail recursion, as in the original sample, and if I move the "msg += a" operation after the recursive call. (notice that python does not feature tail-call optimizations)


async def async_build_message_OK(msg, counter):
    if counter == 0:
        return msg
    print(msg[0])
    # It works fine, both if implemented as Tail recursion:
    # msg += "a"
    # return await asyncio.create_task(async_build_message_2(msg, counter-1))
    
    # or as Non Tail recursion:
    msg = await asyncio.create_task(async_build_message_OK(msg, counter-1))
    msg += "a"
    return msg


OK, that works, nice, but why does my first approach fail? Well, it took me time to understand it, but it makes sense now. First, the stack managed by the python interpreter representing the python function calls is kept in the heap as a list of stackframe objects (then for sure the native CPython code of interpreter uses a "normal stack" but it manages the python code list of stackframes as a stack, with a limit of how many frames we can have in it). Second, generators (and as I've just said, native coroutines are based on generators) are implemented in python in a very interesting way:

the key thing to remember is that python generators encapsulate a stack frame and a code object. The stack frame is allocated on heap memory and holds a pointer to the last bytecode instruction that was run on a code object within the context of the stack frame. It is the last instruction pointer which tells the CPython interpreter which line to execute next when a generator function is resumed. These are the core building blocks over which generator functions thrive.

So each native coroutine is linked to a stack frame, and even if I'm suspending the coroutine first with await asyncio.sleep() as I resume it and do the recursive call, the coroutines stack up, along with their stack frames. When I stack up 1000 of them, the stack crashes.

Once we have managed to understand why the first attempt was failling, we shoud also try to understand why the second attempt, using create_task works! The thing is that in that case we have coroutines, but they dont form a stack of coroutine calls, because each of them has been started by a task, so in the end up with a chain of Tasks in the heap (with each task managing a coroutine), not with a chain/stack of couroutines. Those chained tasks (or Futures) are chained with callbacks set by add_done_callback and that will be invoked by the eventloop as the corresponding set_result is invoked. So the call to asyncio.create_task to execute the recursive call prevents the creation of a stack of coroutines. Then, when the last coroutines ends and returns a value it sets the result of its controlling task, so the eventloop schedules the Task that was controlling the coroutine that created that task (its task._step() method as I previously mentioned), and it will resume the coroutine, executing the "post-recursion code". This in turns finishes the coroutine, that sets the result of its controlling task, and so on.

Thinking it twice, this python solution is doing just what my JavaScript code from last year does implicitly. As JavaScript async functions always return a Promise, the recursive calls are not directly linked, but linked through those returned Promises, so no stack of calls and no stack overflow.

By the way, Javascript generators seem to be implemented in a very similar way to Python ones, as explained here. The data is kept between the different invokations (pause/resume) of the generator by means of maintaining the ExecutionContext object between calls, and a pointer to the current position in the code is also kept. Additionally, async-await in JavaScript seems also to be based on generators

This iterator object is used to control the generator function and keeps alive the execution context of the generator function. The iterator object exposes a [[GeneratorLocation]] property which I assume keeps track of where the program execution of the generator is paused.

The Python and JavaScript implementations are pretty interesting when you come from a C# background, because in .Net generators (oddly called iterators...) are implemented through tons of compiler magic that ends up creating sort of state machines.

Thursday, 29 September 2022

La Rouviere

This is one more post praising Marseille as an amazingly beautiful city. Obviously I'm talking about the architecture and the natural landscape, and keeping aside the "Human landscape": criminality, drug dealing, Islamism, vandalism, far-left Fascist scum (calling themselves antifascists...). If you get to Marseille by train when you get out of the pretty nice Saint Charles Station probably the first thing that you'll see (apart from some Maghrebian and Sahelian dealers) is a small mountain not so far away crowned by a gorgeous Neo-Bizantine church. That's Notre-Dame de la Garde, the magnificent church that dominates the whole city, looking after its inhabitants (well, I hope only for those that deserve it). Additionally the stairs that go down from the station to Boulevard d'Athens to take to you to the city center (the station is in a slightly elevated position) are just imposing, with those two mesmerizing female sculptures representing the African colonies and the Asian colonies of the good, old France (the day some indigenist-anticolonial-"so called antifascist" scumbags will try to damage them "je vais peter un cable").

When you climb up to La Bonne Mere (as the Notre-Dame de la Garde is commonly known), it's not just the building what will marvel you, it's the amazing views. I'm used to cities, Lisbon is the perfect example, where you climb up to some view point to see from there one part of the city. Then you climb up to another one with a different orientation to see another part of the city, and so on. But the 150 meters high mountain hosting Notre Dame de la Garde is just in the center of Marseille, and you can walk around the church to see the whole city, the sea and the islands to the West, the massive city to the North and the East (surrounded by an important mountain range), the imposing Calanques to the South... it's just outstanding.

Looking South-East you'll see some groups of high-rise residential buildings (grand-ensembles) that seem to climb up the mountains. For an urban freak it's a beatiful image. There are several groups of them (each group is a "cité") but one one of them is really massive. I had always assumed it would be some HLM (public housing) nightmare, totally controlled by Islamists and dealers, like a sort of Afghanistan only that dealing cocaine and weed to the locals (infidels from other neighbourhoods) rather than exporting opium to far away countries. Some days ago I decided to investigate and to my total surprise it's not like that at all!

That grand-ensemble is called La Rouviere and is not a public housing mess, but "normal housing", with most of the flats being owned by those living in them. The access to the residential area (or at least to the buildings themselves) is controlled by guardians, so it's a peaceful community of almost 9000 people. The area was built in the 60's and filled mainly by Pieds noir, the French population that had to flee Algeria when it gained independence, after having lived there for generations, after having built a modern and well functioning society and economy that would collapse in just a few years of "independence".

It's not surprising to find an "article" in that left-wing propaganda pamphlet called "Le Monde" where the people living in La Rouviere are called racists and fascists for doing their best to keep their cité peaceful and European by promoting that the new inhabitants that get there when some flat becomes vacant have the same "cultural codes" that those living there. Oh, yes, it's terrible. Moreover, bearing in mind that many of the current inhabitants descend from the Pieds Noirs that first got to the cité, one should for sure ask them to welcome Algerians into the community. It would be a very progressist way to respond to the "la valise ou le cercueil" (the suitcase or the coffin) choice given by Algerians to the Pied-Noirs in 1962... Hopefully some people still have common sense, have learnt lessons from recent history and do not accept the woke, progressist crap.

By the way, this wikipedia article about Marseille's urbanism is really interesting.

Saturday, 24 September 2022

European Hypocrisy, European Suicide

Equality and Unequality are terms constantly and dangerously used by "progressist" people. Equality seems like a nice idea, but when it's constantly applied, not partially. You should treat equally, give equal opportunities, to those that deserve them equally based on their behaviour, effort, attitude...

Little after the start of the War between USA/NATO and Russia in Ukraine, some NGO's, immigrationists, well thinking people... took almost no time to complain about the different attitude of European authorities and European populations towards refugees depending on whether they come from Ukraine or from a non European country. I agree with this different treatment, cause I don't think all migratory waves are the same. So this behaviour shows that it seems like regardless of the multicultural propaganda some people still have some common sense. Europe should be more open to immigration coming from a country with a cultural background similiar to ours, like Ukraine (or Russia if it were the case) than to people coming from countries with a totally differente culture (mainly coming from a totally different religion). The first ones will easily integrate and that fraction of them that will cause problems will do it in the same proportion as local population does cause problems. As for the second ones, for sure some of them will integrate and contribute, but too many of them will not. So it's obvious that we should not welcome them with the same openness.

One should wonder why Europe has not reacted in the same way to the invasion of Ukraine by Russia that to the invasion of Northern Cyprus by Turkey. OK, that invasion took place in 1974, but it continues to this date, so it's not late to kick off the Ottoman scum from that European island.

One could also wonder why the European reponse to the the Ukraine-Donbass conflict and the Serbia-Kosovo conflict has been so different. On both cases we have one region that wants to break away from one country, for some time in the future joining another country (Russia or Albania). Indeed, there are some massive differences between Kosovo and Donbass. Kosovo is the birth place of Serbia, but during Ottoman rule Muslim Albanians started to migrate into the area and at some point these invaders (I call them invaders because they don't have the same religion and culture as the original Serbian population) became majoritary and started to ask for independence (and kill Serbs). Oddly enough, in this case Europe did not remain neutral, but sided with the breakaway region (and as part of that NATO bombed Serbian cities like Belgrad). Ukraine has a quite different history from Serbia and what is now called Ukraine (excluding Crimea, that was only part of Ukraine a few decades due to an odd decision by Krushev) was never a country until last century. I support people's right to decide to form a new country or to join another country, always that there is some sort of historical logic and fairness, but this goes in both senses, the right of Ukraine to exist, and the right of the Donbass to break away and join Russia.

One could also wonder why the European Union does not seem to give a shit about Azerbaijan and Turkey (both Muslims countries of Turkic ethnicity) slaughtering the Christian Armenians in October 2020 and now again. Notice that it was not only the Azeri army and Turkish militia, but also yihadist groups relocated from Syria by the Turkish government.

It's clear to me that in a conflict between countries of different civilizations (Christian, European Cyprus vs Muslim Turks, Christian, Slav Serbs vs Muslim Albanians, Christian Armenians vs Muslim Turks) we should actively support those belonging to our same civilization. In the case of the Donbass, Ukraine and Russia, where Orthodox Christian Slavs are fighting Orthodox Christian Slavs, I don't think we should support any side. What we must do is help innocent civilians and force all parts to sit on a table and negotiate peace. If that peace means and independent Donbass, a Russian Donbass or an Autonomous Donbass in Ukraine (where Russian language and culture would be respected), that should not be a main concern for us.

What for sure we should not do is turn the USA puppet, psychopath clown Zelensky into a hero. That nationalist "hero" prefers to see his whole country being destroyed than to negotiate with Russia the future of one part of that country where an important percentage of the population feel much closer to Moscow than to Kyiv... That psychopath has been pushing all of Europe into a war with Russia. An Ukranian "patriot" that defends the interests of the USA rather than the lives of his people. An American agent that has helped to finish off our relation with Russia and to achieve the complete vassalization of Europe to USA... Hope one day he will pay for all what he has done to his own people and to the rest of Europe.

Monday, 19 September 2022

Python AsyncExecutor

After writing my JavaScript AsyncExecutor it became clear that at some point I would have to do a Python AsyncExecutor :-)

I already did a very basic post some months ago about the differences between JavaScript and Python async/await. The way async/await, Native Coroutines, Tasks and Futures works in Python seems more complex to me than async/await and Promises in Javascript, though maybe it's just that I'm more familiar with async code in JavaScript.

Anyway, implementing an AsyncExecutor is not much different. In Python we have not just Promises as awaitable objects, but Native Coroutine objects, Futures and Tasks (that indeed derive from Future). So we submit to the AsyncExecutor "awaitable functions" (functions that return any of those awaitable objects), along with its parameters. The AsyncExecutor returns a Future, that will be resolved (will have its result set) when the corresponding asynchronous function completes. The _run_action() method takes care of running the "awaitable function" when a slot is available. _run_action is a Native Coroutine, so when we invoke it either from submit() (for the first submitted functions, before filling the available slots) or from _process_result() (as slots are released), 2 functions that are not coroutines themselves, we have to use asyncio.create_task(self._run_action(action)). This is the main difference with the JavaScript version, where functions marked as async are executed immediatelly, with no need of awaiting them (or creating a task). I copy the code here (and I have also uploaded it to a gist)


import asyncio
from dataclasses import dataclass
import random
from typing import Any
import json

@dataclass
class AsyncAction:
    future: asyncio.Future
    awaitable_fn: Any # function that returns an awaitable (coroutine, a task...)
    args: list[Any]
    kwargs: list[Any]


class AsyncExecutor:
    def __init__(self, event_loop: asyncio.AbstractEventLoop, max_running_actions: int):
        self.event_loop = event_loop
        self.max_running_actions = max_running_actions
        self.running_counter = 0
        self.not_launched_actions = []

    def submit(self, awaitable_fn, *args, **kwargs) -> asyncio.Future:
        """
        receives a function to be executed when there's one available slot. That function returns and awaitable
        """
        future = self.event_loop.create_future()
        action = AsyncAction(future, awaitable_fn, args, kwargs)
        if self.running_counter < self.max_running_actions:
            self.running_counter += 1
            # _run_action returns a coroutine, so if I'm not awaiting it need to run it as a task
            #self._run_action(action)
            asyncio.create_task(self._run_action(action))
        else:
            self.not_launched_actions.append(action)
        return future

    async def _run_action(self, action: AsyncAction):
        result = await action.awaitable_fn(*(action.args), **(action.kwargs))
        self._process_result(action, result)

    def _process_result(self, action: AsyncAction, result: Any):
        self.running_counter -= 1
        action.future.set_result(result)
        if len(self.not_launched_actions):
            self.running_counter += 1
            asyncio.create_task(self._run_action(self.not_launched_actions.pop(0)))           


async def mock_download(url: str, delay: int):
    print("starting mock download")
    await asyncio.sleep(delay)
    return url.upper()

def create_download_task(url: str, delay: int):
    print(create_download_task.__name__)
    return asyncio.get_running_loop().create_task(mock_download(url, delay))
 

async def main():
    async_executor = AsyncExecutor(asyncio.get_running_loop(), 4)
    futures = []
    for i in range(0,10):
        delay = random.randint(1, 4)
        if i % 2 == 0:
            future = async_executor.submit(mock_download, f"www.jesoutienslapolice.fr/post_{i}", delay)
        else:
            future = async_executor.submit(create_download_task, f"www.jesoutienslapolice.fr/post_{i}", delay)

        future.add_done_callback(lambda fut: print(f"{fut.result()} done"))
        futures.append(future)
    future = async_executor.submit(mock_download, f"www.jesoutienslapolice.fr/post_{i}", delay)
    future.add_done_callback(lambda fut: print(f"{fut.result()} done"))
    futures.append(future)
    print(f"{len(futures)} submitted")

    results = await asyncio.gather(*futures)
    print(f"all finished: {json.dumps(results, indent=4)}")


asyncio.run(main())

As we've seen, asyncio.create_task is really important in python's asynchronous code. This discussion helps to understand what it does.

It submits the coroutine to run "in the background", i.e. concurrently with the current task and all other tasks, switching between them at await points. It returns an awaitable handle called a "task" which you can also use to cancel the execution of the coroutine.

It's one of the central primitives of asyncio, the asyncio equivalent of starting a thread. (In the same analogy, awaiting the task with await is the equivalent of joining a thread.)

Friday, 16 September 2022

Le Brasilia

At the start of this post from 1 year ago I mention what a beautiful city Marseille is and how much I love its urban landscape. Of course you have tons of classic stuff, of course you have quite a few recent pieces of interest. It's not just the tour CMA/CGM or Jean Nouvel's Marseillese tour (that indeed I don't find particularly appealing) but the whole Euromediterranee neighbourhood with its offices and hotels, the long avenue with the tram running along it, the 15-20 stories residential buildings, etc that makes a really pleasant example of how a modern neighbourhood in a European city should look.

Tours CMA/CGM et La Marseillese

Moving a few decades back, we find that also at that time Marseille did some good efforts to look like a modern city and reclaim its place as a Mediterranean capital. There are 2 high-rise buildings (for European standards they are skyscrappers) from the 70's that (slightly surpassing or almost reaching the 100 meters mark) will catch your eye when looking eastwards from Notre Dame de la Garde (la Belle Mere), the residential tower Le Grand Pavois and the offices tower Tour Mediterranee. They are the common example of architecture of the 70's that pop up in many European cities of similar size, trying to create a strong architectural signal.

Though I consider that kind of towers pretty interesting, I understand that for most people (that are not urban architecture freaks like me) those buildings have hardly any interest, and the only reference to architecture of the 50's-60's-70's in Marseille that you'll read about is La Cité Radieuse, designed by Le Corbusier. I'm not a fan at all of this guy, but I think his concept of Unité d'Habitation has some interest. For sure paying a visit to La Cité Radieuse is a good idea, but I would not give it much precedence in a list of things to see and do in Marseille. Indeed, it was not until my eighth visit to the city that I decided to go to see it.

That time in 2018, when walking along Boulevard Michelet from Rond-Point du Prado on my way to Cité Radieuse, one imposing building on the right deeply caught my eye, mainly because of its sculptural outer stairs (fire escape). I did not think about it anymore since then, but last week when I was again in Marseille and I happened to be around the Rond-Point du Prado area, I decided to take another look to that surprising building. I found it even more impressive than the first time, and I found out that is called "Le Brasilia". The Ministére de la Culture site has an interesting entry about the building, giving some pretty good information. I think the building is like 22 stories high, but it seems it had been initially planned to reach 30 stories, which would have made it even more impressive. The architect, Fernand Boukobza was pretty much influenced at that time by Le Corbusier and La Cité Radieuse, and probably this is his most important work. It's interesting that he designed several HLM ensembles, the terrifying French social housing complexes (I say terrifying because of part of the population living there, not because of the architecture, that in many cases seems pretty decent to me). Indeed, it seems like he designed the infamous La Castellane, that has become one of the worst neighbourhoods in Europe.

Le Brasilia