<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>devlife &amp;mdash; StealthyCoder</title>
    <link>https://stealthycoder.writeas.com/tag:devlife</link>
    <description>Making code ninjas out of everyone</description>
    <pubDate>Sat, 04 Jul 2026 16:13:54 +0000</pubDate>
    <item>
      <title>Small change, big difference</title>
      <link>https://stealthycoder.writeas.com/small-change-big-difference?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Sometimes the small things in life can make the biggest difference. This time it is a small adventure in Python. !--more--&#xA;&#xA;I was just working on a small PoC (Proof of Concept) to do some nginx testing. I wanted to have a working nginx, two simple Python APIs and then a client that would send requests to the nginx instance that would be load balancing to either of those Python API instances. For some reason it would not collect the statistics correctly. Hereunder a simplified example but it will contain the core concept of what I was trying to do.&#xA;&#xA;Concurrency&#xA;&#xA;So in Python it is very simple to do concurrency. You get an executor and then it will just be available. In code:&#xA;&#xA;from concurrent.futures import ProcessPoolExecutor&#xA;&#xA;var = 0&#xA;&#xA;def func(var):&#xA;    var = var + 1&#xA;    print(var)&#xA;&#xA;with ProcessPoolExecutor() as pool:&#xA;    for  in range(10):&#xA;        pool.submit(func, var)&#xA;&#xA;print(var)&#xA;&#xA;If you run it you will see something weird. The var will be constantly 1 and the last one will be 0. What is going on here?&#xA;&#xA;Processes&#xA;&#xA;The key indicator is the ProcessPoolExecutor it will create a separate instance/interpreter/process and run the code inside there. So that var will be complete different instance as well. It took some time for me to realize this. So how to fix this? Either switch to ThreadPoolExecutor, or do the following:&#xA;&#xA;from concurrent.futures import ProcessPoolExecutor, wait&#xA;from multiprocessing.managers import SyncManager&#xA;&#xA;var = 0&#xA;&#xA;def updatevar():&#xA;    global var&#xA;    var = var + 1&#xA;&#xA;manager = SyncManager(address=(&#39;&#39;, 5566), authkey=b&#34;secret&#34;)&#xA;manager.register(&#39;updatevar&#39;, callable=updatevar)&#xA;manager.register(&#39;getvar&#39;, callable=lambda: var)&#xA;manager.start()&#xA;&#xA;def func():&#xA;    m = SyncManager(address=(&#39;&#39;, 5566), authkey=b&#34;secret&#34;)&#xA;    m.register(&#34;updatevar&#34;)&#xA;    m.connect()&#xA;    m.updatevar()&#xA;    m.shutdown()&#xA;&#xA;futs = []&#xA;with ProcessPoolExecutor() as pool:&#xA;    for  in range(10):&#xA;        futs.append(pool.submit(func))&#xA;wait(futs)&#xA;print(manager.getvar())&#xA;manager.shutdown()&#xA;&#xA;That is quite the transformation. So in essence what we need extra is a Manager to synchronize between processes. One for updating the var and one to get the var. &#xA;&#xA;Then every function needs to connect to the Manager and then register everything double. &#xA;&#xA;If one wants to get rid of the global var then you could make a simple class that holds state and instantiate one to the manager as well. &#xA;&#xA;Conclusion&#xA;&#xA;Sometimes something simple turns out to be quite complicated. A bonus solution would be to use sharedmemory. That could be something for another post.&#xA;&#xA;#devlife #python]]&gt;</description>
      <content:encoded><![CDATA[<p>Sometimes the small things in life can make the biggest difference. This time it is a small adventure in Python. </p>

<p>I was just working on a small PoC (Proof of Concept) to do some <code>nginx</code> testing. I wanted to have a working <code>nginx</code>, two simple Python APIs and then a client that would send requests to the <code>nginx</code> instance that would be load balancing to either of those Python API instances. For some reason it would not collect the statistics correctly. Hereunder a simplified example but it will contain the core concept of what I was trying to do.</p>

<h1 id="concurrency" id="concurrency">Concurrency</h1>

<p>So in Python it is very simple to do concurrency. You get an executor and then it will just be available. In code:</p>

<pre><code class="language-python">from concurrent.futures import ProcessPoolExecutor

var = 0


def func(var):
    var = var + 1
    print(var)


with ProcessPoolExecutor() as pool:
    for _ in range(10):
        pool.submit(func, var)

print(var)
</code></pre>

<p>If you run it you will see something weird. The var will be constantly <code>1</code> and the last one will be <code>0</code>. What is going on here?</p>

<h1 id="processes" id="processes">Processes</h1>

<p>The key indicator is the <code>ProcessPoolExecutor</code> it will create a separate instance/interpreter/process and run the code inside there. So that <code>var</code> will be complete different instance as well. It took some time for me to realize this. So how to fix this? Either switch to <code>ThreadPoolExecutor</code>, or do the following:</p>

<pre><code class="language-python">from concurrent.futures import ProcessPoolExecutor, wait
from multiprocessing.managers import SyncManager

var = 0


def update_var():
    global var
    var = var + 1


manager = SyncManager(address=(&#39;&#39;, 5566), authkey=b&#34;secret&#34;)
manager.register(&#39;update_var&#39;, callable=update_var)
manager.register(&#39;get_var&#39;, callable=lambda: var)
manager.start()


def func():
    m = SyncManager(address=(&#39;&#39;, 5566), authkey=b&#34;secret&#34;)
    m.register(&#34;update_var&#34;)
    m.connect()
    m.update_var()
    m.shutdown()


futs = []
with ProcessPoolExecutor() as pool:
    for _ in range(10):
        futs.append(pool.submit(func))
wait(futs)
print(manager.get_var())
manager.shutdown()
</code></pre>

<p>That is quite the transformation. So in essence what we need extra is a <code>Manager</code> to synchronize between processes. One for updating the <code>var</code> and one to get the <code>var</code>.</p>

<p>Then every function needs to connect to the <code>Manager</code> and then register everything double.</p>

<p>If one wants to get rid of the <code>global var</code> then you could make a simple class that holds state and instantiate one to the <code>manager</code> as well.</p>

<h1 id="conclusion" id="conclusion">Conclusion</h1>

<p>Sometimes something simple turns out to be quite complicated. A bonus solution would be to use <code>shared_memory</code>. That could be something for another post.</p>

<p><a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a> <a href="https://stealthycoder.writeas.com/tag:python" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">python</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/small-change-big-difference</guid>
      <pubDate>Thu, 13 Jun 2024 21:41:07 +0000</pubDate>
    </item>
    <item>
      <title>Stacking past tech</title>
      <link>https://stealthycoder.writeas.com/stacking-past-tech?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[What a weird tech stack I made recently. I already wrote on this a bit. Now I come back for some more. !--more--&#xA;&#xA;Parts in question&#xA;&#xA;I have a Compaq Pro Linea 4/33 on the bottom. on top of which is the JVC SEA-33. That in turn has a Denon DRM-555 on it, followed by a a Sharp VHS player/recorder. &#xA;&#xA;So the Compaq is a desktop computer sporting a 486 DX2 processor. Then the JVC is a graphic equalizer. The Denon is a cassette deck player/recorder and then a VHS recorder. &#xA;&#xA;The VHS recorder has a SCART connection into which is inserted a device to be able to capture the video and make it possible to output that to my current modern computer and actually record/digitalize the video. &#xA;&#xA;Connection of the audio&#xA;&#xA;I wanted to be able to have the cassette deck be able to easily record tapes so I can make some more mixtapes for in the car for road trips. Luckily for me the manual is easy enough to find online and I started out hooking things up. Nothing worked.&#xA;&#xA;I have the headphone jack of my laptop go out into the SEA-33 and then the SEA-33 goes into a jack to my actual headphones. I hooked the SEA-33 up with the tape deck Play and Rec lines like on the diagram. Nothing. Then I hooked the tape deck up with the line directly from my headphone jack. Nothing again. &#xA;&#xA;I got frustrated and then I remembered something important. These are analog systems. So if you sent a digital signal that is already low powered they are not receiving anything. In essence if I sent volume at 5% then the signal is too low to do anything with it. So I upped the volume to something like 50% and then I saw the lines moving on the tape deck. Success!&#xA;&#xA;So I hooked it up with the Play and Rec lines, and it still worked. Perfect. Now I am pretty sure I can listen to the tape deck whilst recording. However what I can do now is bypass my headphones. So I can up the volume and my headphones do not break. &#xA;&#xA;So yeah, I now have a multimedia tower of epic proportions and definitely would have been so cool back in the day. What a weird mix of technologies. &#xA;&#xA;As a side note I run from the tape deck head phone jack a cable to my mic input of another laptop to be able to digitalize old tapes my parents in law had lying around. So it goes:&#xA;&#xA;headphone jack Dell XPS-15 --  SEA-33 Line In --  SEA-33 Rec --  Denon DRM-555 Line In --  Denon DRM-555 headphone jack --  Mic jack Sony Vaio&#xA;&#xA;#devlife #oldtech&#xA;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>What a weird tech stack I made recently. I already wrote on <a href="https://stealthycoder.writeas.com/investing-in-the-past" rel="nofollow">this a bit</a>. Now I come back for some more. </p>

<h2 id="parts-in-question" id="parts-in-question">Parts in question</h2>

<p>I have a <strong>Compaq Pro Linea 4/33</strong> on the bottom. on top of which is the <strong>JVC SEA-33</strong>. That in turn has a <strong>Denon DRM-555</strong> on it, followed by a a <strong>Sharp VHS player/recorder</strong>.</p>

<p>So the Compaq is a desktop computer sporting a <em>486 DX2</em> processor. Then the JVC is a graphic equalizer. The Denon is a cassette deck player/recorder and then a VHS recorder.</p>

<p>The VHS recorder has a SCART connection into which is inserted a device to be able to capture the video and make it possible to output that to my current modern computer and actually record/digitalize the video.</p>

<h2 id="connection-of-the-audio" id="connection-of-the-audio">Connection of the audio</h2>

<p>I wanted to be able to have the cassette deck be able to easily record tapes so I can make some more mixtapes for in the car for road trips. Luckily for me the manual is easy enough to find online and I started out hooking things up. Nothing worked.</p>

<p>I have the headphone jack of my laptop go out into the <em>SEA-33</em> and then the <em>SEA-33</em> goes into a jack to my actual headphones. I hooked the <em>SEA-33</em> up with the tape deck <strong>Play</strong> and <strong>Rec</strong> lines like on the diagram. Nothing. Then I hooked the tape deck up with the line directly from my headphone jack. Nothing again.</p>

<p>I got frustrated and then I remembered something important. These are analog systems. So if you sent a digital signal that is already low powered they are not receiving anything. In essence if I sent volume at 5% then the signal is too low to do anything with it. So I upped the volume to something like 50% and then I saw the lines moving on the tape deck. Success!</p>

<p>So I hooked it up with the <strong>Play</strong> and <strong>Rec</strong> lines, and it still worked. Perfect. Now I am pretty sure I can listen to the tape deck whilst recording. However what I can do now is bypass my headphones. So I can up the volume and my headphones do not break.</p>

<p>So yeah, I now have a multimedia tower of epic proportions and definitely would have been so cool back in the day. What a weird mix of technologies.</p>

<p>As a side note I run from the tape deck head phone jack a cable to my mic input of another laptop to be able to digitalize old tapes my parents in law had lying around. So it goes:</p>

<pre><code>headphone jack Dell XPS-15 --&gt; SEA-33 Line In --&gt; SEA-33 Rec --&gt; Denon DRM-555 Line In --&gt; Denon DRM-555 headphone jack --&gt; Mic jack Sony Vaio
</code></pre>

<p><a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a> <a href="https://stealthycoder.writeas.com/tag:oldtech" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">oldtech</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/stacking-past-tech</guid>
      <pubDate>Sat, 25 May 2024 22:48:00 +0000</pubDate>
    </item>
    <item>
      <title>Strawberry Fields</title>
      <link>https://stealthycoder.writeas.com/strawberry-fields-ln3t?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I was working on getting some work ported over to Strawberry from Graphene-Django, and I suddenly hit a snag. Once I found out what happened to the Strawberry Fields, I was just glad I could solve it at that point. !--more--&#xA;&#xA;The premise&#xA;&#xA;So the premise is that I worked on a project that was written with Graphene Django, for Django obviously, that essentially is the Python framework implementation behind a GraphQL schema. So you have a GraphQL endpoint where you send your queries and mutations and it will validate the schema and execute the queries and mutations. The two problems with the framework are it is not fully async and it is a lot of meta class programming that implies a lot of configuring but not a lot of control over the implementation details.&#xA;&#xA;This meant when we were hitting our performance issues, we tried everything from low-hanging fruit like using Dataloaders, to optimizing certain queries by not relying on the ORM (Graphene Mongo in this case) and just using PyMongo directly. &#xA;&#xA;Still we got stuck again, and the fact we relied heavily on the Promise implementation in Python to match the Promises/A+ from Javascript land did not help actually. Also there tried to improve some things by making things more streamlined in their concurrency, but alas. So the move is to another framework, preferably in Python, to keep the current dev team. &#xA;&#xA;Enter Strawberry&#xA;&#xA;So Strawberry is a nice framework that does both sync and async and it is a lot of configuring as well, but also lets you control the implementation details. For instance through things called FieldExtensions. These can either be run when the schema is first generated (through the apply method) or when the nodes are being resolved (through either sync resolve or async resolveasync functions). This is a wonderful way to, through a middleware type approach, have a way to tweak the implementation details. &#xA;&#xA;One of the things that kind of was lacking in the Graphene Django implementation setup was a nice automatic control of what fields were allowed to be given as filters. Ideally it should just be always the fields exposed on the Node itself. That however was not the case, you had to manually make it so. Trying to make the new stack better and fixing that particular nuisance, I made a simple BaseExtension class:&#xA;&#xA;class BaseExtension(FieldExtension):&#xA;&#xA;    def apply(self, field: StrawberryField) -  None:&#xA;        self.filterfields = []&#xA;        resolvedtype: Type[WithStrawberryObjectDefinition] = cast(&#xA;            Type[WithStrawberryObjectDefinition], field.resolvetype()&#xA;        )&#xA;        if resolvedtype.strawberrydefinition_.specializedtypevarmap:&#xA;            node = cast(&#xA;                Type[WithStrawberryObjectDefinition],&#xA;                resolvedtype.strawberrydefinition_.specializedtypevarmap[&#xA;                    &#34;NodeType&#34;&#xA;                ],&#xA;            )&#xA;&#xA;        for f in node._strawberrydefinition_.fields:&#xA;            field.arguments.append(&#xA;                StrawberryArgument(&#xA;                    pythonname=f.name,&#xA;                    graphqlname=f.name.replace(&#34;&#34;, &#34;&#34;)&#xA;                    if f.name.startswith(&#34;&#34;)&#xA;                    else None,&#xA;                    typeannotation=StrawberryAnnotation(&#xA;                        Optional[f.type]&#xA;                        if not isinstance(f.type, StrawberryOptional)&#xA;                        else Optional[f.type.oftype]&#xA;                    ),&#xA;                    description=&#34;&#34;,&#xA;                    default=strawberry.UNSET,&#xA;                )&#xA;            )&#xA;            self.filterfields.append(f.name)&#xA;&#xA;All this really does is go over the fields defined and add them all as Arguments so that you can filter on them and they will be passed along as kwargs in the resolve functions. &#xA;&#xA;Perfect.&#xA;&#xA;Snag time&#xA;&#xA;So I was porting the NodeTypes and this project also uses Relay. It is a certain implementation of GraphQL itself. Not very important, except for me to say now I had not made any connections yet. As in one Node -  another Node. Which is quite common in GraphQL and in Relay. &#xA;&#xA;When I made the first connection, the schema would not even generate. I was so frustrated, and nothing worked. I could go from resolver function -  relay.ListConnection[NodeType] but not from Node -  relay.ListConnection[NodeType]. It kept complaining about it not being a GraphQLInput Type. I did not want it as an input. I struggled and looked deep into the source code of everything, trying to hack it there. Making it dynamically an input or an output depending on properties, and I suddenly stopped. Since there was no mention of this online whatsoever it had to be a problem I caused and created. &#xA;&#xA;I went to bed, late. Woke up. Paced around a bit. &#xA;In my head I thought, why is it automatically turning into an argu.....oh I am an idiot. &#xA;&#xA;So I revisited my BaseExtension field that powered my dynamic argument adding stuff. I tweaked it here and there and the following is the fixed version:&#xA;&#xA;class BaseExtension(FieldExtension):&#xA;    filterfields = [&#34;projectid&#34;, &#34;changeorderid&#34;]&#xA;&#xA;    def apply(self, field: StrawberryField) -  None:&#xA;        self.filterfields = [&#34;projectid&#34;, &#34;changeorderid&#34;]&#xA;        resolvedtype: Type[WithStrawberryObjectDefinition] = cast(&#xA;            Type[WithStrawberryObjectDefinition], field.resolvetype()&#xA;        )&#xA;        if resolvedtype.strawberrydefinition_.specializedtypevarmap:&#xA;            node = cast(&#xA;                Type[WithStrawberryObjectDefinition],&#xA;                resolvedtype.strawberrydefinition_.specializedtypevarmap[&#xA;                    &#34;NodeType&#34;&#xA;                ],&#xA;            )&#xA;        else:&#xA;            node = resolvedtype&#xA;&#xA;        for f in node.strawberrydefinition_.fields:&#xA;            if inspect.isclass(f.type) and issubclass(&#xA;                f.type, strawberry.relay.types.ListConnection&#xA;            ):&#xA;                continue&#xA;            if isinstance(f.type, StrawberryOptional):&#xA;                if inspect.isclass(f.type.oftype) and issubclass(&#xA;                    f.type.oftype, strawberry.relay.types.ListConnection&#xA;                ):&#xA;                    continue&#xA;            field.arguments.append(&#xA;                StrawberryArgument(&#xA;                    pythonname=f.name,&#xA;                    graphqlname=f.name.replace(&#34;&#34;, &#34;&#34;)&#xA;                    if f.name.startswith(&#34;&#34;)&#xA;                    else None,&#xA;                    typeannotation=StrawberryAnnotation(&#xA;                        Optional[f.type]&#xA;                        if not isinstance(f.type, StrawberryOptional)&#xA;                        else Optional[f.type.oftype]&#xA;                    ),&#xA;                    description=&#34;&#34;,&#xA;                    default=strawberry.UNSET,&#xA;                )&#xA;            )&#xA;            self.filterfields.append(f.name)&#xA;&#xA;Essentially what I needed to do was check if the type or of_type is a class. If it is check if it is a relay.ListConnection type class and then exclude it from the argument generation. All worked right after this. &#xA;&#xA;Conclusion&#xA;&#xA;I really like this framework. It gives me insight into how they operate and why sometimes a particular query is slow, and they give you the space to fix it. For example I already fixed the fact that we can load all the necessary subparts in one go from a node with the Dataloaders. That was not possible before. However it was still as slow as the old stack, because each node on it&#39;s own tried to create this new relay.ListConnection for one Edge essentially. &#xA;&#xA;We already have all the instances needed to make all the edges when doing the Dataloader logic, so implement in that particular spot also the creation of all the edges in one go. Then have a simple mapping of node.id -  Edge and you are done. This sped up things by quite a significant margin. &#xA;&#xA;Something the old stack could not really do. It had no real way of giving you the same tools to do the same thing. &#xA;&#xA;#devlife #python #graphql]]&gt;</description>
      <content:encoded><![CDATA[<p>I was working on getting some work ported over to <a href="https://strawberry.rocks" rel="nofollow">Strawberry</a> from Graphene-Django, and I suddenly hit a snag. Once I found out what happened to the Strawberry Fields, I was just glad I could solve it at that point. </p>

<h2 id="the-premise" id="the-premise">The premise</h2>

<p>So the premise is that I worked on a project that was written with Graphene Django, for Django obviously, that essentially is the Python framework implementation behind a GraphQL schema. So you have a GraphQL endpoint where you send your queries and mutations and it will validate the schema and execute the queries and mutations. The two problems with the framework are it is not fully <code>async</code> and it is a lot of meta class programming that implies a lot of configuring but not a lot of control over the implementation details.</p>

<p>This meant when we were hitting our performance issues, we tried everything from low-hanging fruit like using Dataloaders, to optimizing certain queries by not relying on the ORM (Graphene Mongo in this case) and just using PyMongo directly.</p>

<p>Still we got stuck again, and the fact we relied heavily on the Promise implementation in Python to match the Promises/A+ from Javascript land did not help actually. Also there tried to improve some things by making things more streamlined in their concurrency, but alas. So the move is to another framework, preferably in Python, to keep the current dev team.</p>

<h2 id="enter-strawberry" id="enter-strawberry">Enter Strawberry</h2>

<p>So Strawberry is a nice framework that does both <code>sync</code> and <code>async</code> and it is a lot of configuring as well, but also lets you control the implementation details. For instance through things called <code>FieldExtensions</code>. These can either be run when the schema is first generated (through the <code>apply</code> method) or when the nodes are being resolved (through either <code>sync</code> <code>resolve</code> or <code>async</code> <code>resolve_async</code> functions). This is a wonderful way to, through a middleware type approach, have a way to tweak the implementation details.</p>

<p>One of the things that kind of was lacking in the Graphene Django implementation setup was a nice automatic control of what fields were allowed to be given as filters. Ideally it should just be always the fields exposed on the Node itself. That however was not the case, you had to manually make it so. Trying to make the new stack better and fixing that particular nuisance, I made a simple <code>BaseExtension</code> class:</p>

<pre><code class="language-python">class BaseExtension(FieldExtension):

    def apply(self, field: StrawberryField) -&gt; None:
        self.filter_fields = []
        resolved_type: Type[WithStrawberryObjectDefinition] = cast(
            Type[WithStrawberryObjectDefinition], field.resolve_type()
        )
        if resolved_type.__strawberry_definition__.specialized_type_var_map:
            node = cast(
                Type[WithStrawberryObjectDefinition],
                resolved_type.__strawberry_definition__.specialized_type_var_map[
                    &#34;NodeType&#34;
                ],
            )

        for f in node.__strawberry_definition__.fields:
            field.arguments.append(
                StrawberryArgument(
                    python_name=f.name,
                    graphql_name=f.name.replace(&#34;_&#34;, &#34;&#34;)
                    if f.name.startswith(&#34;_&#34;)
                    else None,
                    type_annotation=StrawberryAnnotation(
                        Optional[f.type]
                        if not isinstance(f.type, StrawberryOptional)
                        else Optional[f.type.of_type]
                    ),
                    description=&#34;&#34;,
                    default=strawberry.UNSET,
                )
            )
            self.filter_fields.append(f.name)
</code></pre>

<p>All this really does is go over the fields defined and add them all as <code>Arguments</code> so that you can filter on them and they will be passed along as <code>kwargs</code> in the <code>resolve</code> functions.</p>

<p>Perfect.</p>

<h2 id="snag-time" id="snag-time">Snag time</h2>

<p>So I was porting the <code>NodeType</code>s and this project also uses <em>Relay</em>. It is a certain implementation of GraphQL itself. Not very important, except for me to say now I had not made any connections yet. As in one Node –&gt; another Node. Which is quite common in GraphQL and in Relay.</p>

<p>When I made the first connection, the schema would not even generate. I was so frustrated, and nothing worked. I could go from <code>resolver function -&gt; relay.ListConnection[NodeType]</code> but not from <code>Node -&gt; relay.ListConnection[NodeType]</code>. It kept complaining about it not being a GraphQLInput Type. I did not want it as an input. I struggled and looked deep into the source code of everything, trying to hack it there. Making it dynamically an input or an output depending on properties, and I suddenly stopped. Since there was no mention of this online whatsoever it had to be a problem I caused and created.</p>

<p>I went to bed, late. Woke up. Paced around a bit.
In my head I thought, why is it automatically turning into an argu.....oh I am an idiot.</p>

<p>So I revisited my <code>BaseExtension</code> field that powered my dynamic argument adding stuff. I tweaked it here and there and the following is the fixed version:</p>

<pre><code class="language-python">class BaseExtension(FieldExtension):
    filter_fields = [&#34;project_id&#34;, &#34;change_order_id&#34;]

    def apply(self, field: StrawberryField) -&gt; None:
        self.filter_fields = [&#34;project_id&#34;, &#34;change_order_id&#34;]
        resolved_type: Type[WithStrawberryObjectDefinition] = cast(
            Type[WithStrawberryObjectDefinition], field.resolve_type()
        )
        if resolved_type.__strawberry_definition__.specialized_type_var_map:
            node = cast(
                Type[WithStrawberryObjectDefinition],
                resolved_type.__strawberry_definition__.specialized_type_var_map[
                    &#34;NodeType&#34;
                ],
            )
        else:
            node = resolved_type

        for f in node.__strawberry_definition__.fields:
            if inspect.isclass(f.type) and issubclass(
                f.type, strawberry.relay.types.ListConnection
            ):
                continue
            if isinstance(f.type, StrawberryOptional):
                if inspect.isclass(f.type.of_type) and issubclass(
                    f.type.of_type, strawberry.relay.types.ListConnection
                ):
                    continue
            field.arguments.append(
                StrawberryArgument(
                    python_name=f.name,
                    graphql_name=f.name.replace(&#34;_&#34;, &#34;&#34;)
                    if f.name.startswith(&#34;_&#34;)
                    else None,
                    type_annotation=StrawberryAnnotation(
                        Optional[f.type]
                        if not isinstance(f.type, StrawberryOptional)
                        else Optional[f.type.of_type]
                    ),
                    description=&#34;&#34;,
                    default=strawberry.UNSET,
                )
            )
            self.filter_fields.append(f.name)
</code></pre>

<p>Essentially what I needed to do was check if the <code>type</code> or <code>of_type</code> is a <code>class</code>. If it is check if it is a <code>relay.ListConnection</code> type class and then exclude it from the argument generation. All worked right after this.</p>

<h2 id="conclusion" id="conclusion">Conclusion</h2>

<p>I really like this framework. It gives me insight into how they operate and why sometimes a particular query is slow, and they give you the space to fix it. For example I already fixed the fact that we can load all the necessary subparts in one go from a node with the Dataloaders. That was not possible before. However it was still as slow as the old stack, because each node on it&#39;s own tried to create this new <code>relay.ListConnection</code> for one <code>Edge</code> essentially.</p>

<p>We already have all the instances needed to make all the edges when doing the Dataloader logic, so implement in that particular spot also the creation of all the edges in one go. Then have a simple mapping of <code>node.id -&gt; Edge</code> and you are done. This sped up things by quite a significant margin.</p>

<p>Something the old stack could not really do. It had no real way of giving you the same tools to do the same thing.</p>

<p><a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a> <a href="https://stealthycoder.writeas.com/tag:python" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">python</span></a> <a href="https://stealthycoder.writeas.com/tag:graphql" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">graphql</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/strawberry-fields-ln3t</guid>
      <pubDate>Tue, 05 Mar 2024 11:58:49 +0000</pubDate>
    </item>
    <item>
      <title>So it is just like JavaScript</title>
      <link>https://stealthycoder.writeas.com/so-it-is-just-like-javascript?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I saw this thing being advertised on HackerNews or some such similar thing. After checking it out, it was just a blatant lie and more than that, it is just trying to sell JavaScript and HTML as they are intended on being used as if they thought of this. !--more-- The library/product in question is this. &#xA;&#xA;Why does it even exist&#xA;&#xA;I am having a hard time figuring out why this even exists as being a thing. It sounds like someone is thinking highly of themselves and go this thing I thought of that nobody has done before needs to be shared with the world and it will make me famous. Then it is something that not only already existed but is used globally and it is the exact same idea that already exists but now somehow transformed into their own idea.&#xA;&#xA;It claims to transform a regular html site into a realtime application by loading in some JavaScript.&#xA;&#xA;That is exactly what JavaScript already does. Not only that, it is apparently realtime, which I think is definitely misused here. &#xA;&#xA;I just do not like these frameworks that try to claim they are superior and did something amazing, where in reality they did nothing really. &#xA;&#xA;Something impressive&#xA;&#xA;If you want to make something truly impressive, use the basic tools that are there and build upon it. Like using construction materials and tools to create a doll house for example. Not try and sell to people see this wood and tools, it can turn any room into one filled with a dollhouse, you just have to create it. &#xA;&#xA;#100DaysToOffload #devlife&#xA;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>I saw this thing being advertised on HackerNews or some such similar thing. After checking it out, it was just a blatant lie and more than that, it is just trying to sell JavaScript and HTML as they are intended on being used as if they thought of this.  The library/product in question is <a href="https://cocreate.app/docs/#introductions" rel="nofollow">this</a>.</p>

<h2 id="why-does-it-even-exist" id="why-does-it-even-exist">Why does it even exist</h2>

<p>I am having a hard time figuring out why this even exists as being a thing. It sounds like someone is thinking highly of themselves and go this thing I thought of that nobody has done before needs to be shared with the world and it will make me famous. Then it is something that not only already existed but is used globally and it is the exact same idea that already exists but now somehow transformed into their own idea.</p>

<p>It claims to transform a regular html site into a realtime application by loading in some JavaScript.</p>

<p>That is exactly what JavaScript already does. Not only that, it is apparently realtime, which I think is definitely misused here.</p>

<p>I just do not like these frameworks that try to claim they are superior and did something amazing, where in reality they did nothing really.</p>

<h2 id="something-impressive" id="something-impressive">Something impressive</h2>

<p>If you want to make something truly impressive, use the basic tools that are there and build upon it. Like using construction materials and tools to create a doll house for example. Not try and sell to people see this wood and tools, it can turn any room into one filled with a dollhouse, you just have to create it.</p>

<p><a href="https://stealthycoder.writeas.com/tag:100DaysToOffload" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">100DaysToOffload</span></a> <a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/so-it-is-just-like-javascript</guid>
      <pubDate>Tue, 10 Jan 2023 09:12:07 +0000</pubDate>
    </item>
    <item>
      <title>There exists more than REST</title>
      <link>https://stealthycoder.writeas.com/there-exists-more-than-rest?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[So these days it seems there are too many choices, and the wrong simplification is being applied. Since everything  in the frontend and backend is being consolidated to a singular language/framework to solve the entire issue. !--more-- I think it should be pulled back a little further and also taken a bit broader. &#xA;&#xA;Remember REST?&#xA;&#xA;Remember when REST first was a big thing. The idea that you would have stateless calls to the backend that just ships data? That was revolutionary. Then all sites became REST API sites. The SPA got borne out of it. Then everything became an SPA. Whether that fit or not. Handling any type of state/session data became extremely difficult and also the calls to get data from the database through some reverse proxies to ultimately end up in the frontend after several transpiling steps made everything worse in most every way.&#xA;&#xA;There are many other ways to program the application. You had the MVC pattern, MVVM or any of the MV* patterns. These still hold value to this day. I think it is being skipped because it is so old and not new. Not exciting, yeah yeah tech is boring is good post coming. Well not really. Try to make tech exciting and challenging, but not just for the sake of making it difficult for you or following a trend that seems to be forming.&#xA;&#xA;Then there is the fact you do the entire thing in either the backend or frontend that seems to be prevailing now. Which brings with it this idea of following the caravan out into the desert where everyone thinks someone else will know the way to the oasis and they all end up dying in the desert because of it.&#xA;&#xA;I have been in so many projects and every time they reached for a web application with a login screen and everything being behind REST API. The next thing that always has to happen is how to secure the REST API? Then we are always building everything twice, the data models get created in the backend and then have to be exposed to the frontend and because that is in TypeScript we have the data model definitions (schemas) now twice. Once in the backend for let&#39;s say Java and Hibernate and in the frontend for Angular. After pointing this out, we still keep on the same train tracks. Why? Well we can charge the customer twice for the same work.&#xA;&#xA;Pick the right tool&#xA;&#xA;Just keep your options open and choose the right tool for the job. Not just take GraphQL which is nothing more than a database explorer disguised as a RPC framework. When I am working on my personal sites, I try to make it as simple as possible and not do double work. So a MVC app works perfect for me. &#xA;&#xA;#100DaysToOffload #devlife]]&gt;</description>
      <content:encoded><![CDATA[<p>So these days it seems there are too many choices, and the wrong simplification is being applied. Since everything  in the frontend and backend is being consolidated to a singular language/framework to solve the entire issue.  I think it should be pulled back a little further and also taken a bit broader.</p>

<h1 id="remember-rest" id="remember-rest">Remember REST?</h1>

<p>Remember when REST first was a big thing. The idea that you would have stateless calls to the backend that just ships data? That was revolutionary. Then all sites became REST API sites. The SPA got borne out of it. Then everything became an SPA. Whether that fit or not. Handling any type of state/session data became extremely difficult and also the calls to get data from the database through some reverse proxies to ultimately end up in the frontend after several transpiling steps made everything worse in most every way.</p>

<p>There are many other ways to program the application. You had the MVC pattern, MVVM or any of the MV* patterns. These still hold value to this day. I think it is being skipped because it is so old and not new. Not exciting, yeah yeah tech is boring is good post coming. Well not really. Try to make tech exciting and challenging, but not just for the sake of making it difficult for you or following a trend that seems to be forming.</p>

<p>Then there is the fact you do the entire thing in either the backend or frontend that seems to be prevailing now. Which brings with it this idea of following the caravan out into the desert where everyone thinks someone else will know the way to the oasis and they all end up dying in the desert because of it.</p>

<p>I have been in so many projects and every time they reached for a web application with a login screen and everything being behind REST API. The next thing that always has to happen is how to secure the REST API? Then we are always building everything twice, the data models get created in the backend and then have to be exposed to the frontend and because that is in TypeScript we have the data model definitions (schemas) now twice. Once in the backend for let&#39;s say Java and Hibernate and in the frontend for Angular. After pointing this out, we still keep on the same train tracks. Why? Well we can charge the customer twice for the same work.</p>

<h1 id="pick-the-right-tool" id="pick-the-right-tool">Pick the right tool</h1>

<p>Just keep your options open and choose the right tool for the job. Not just take GraphQL which is nothing more than a database explorer disguised as a RPC framework. When I am working on my personal sites, I try to make it as simple as possible and not do double work. So a MVC app works perfect for me.</p>

<p><a href="https://stealthycoder.writeas.com/tag:100DaysToOffload" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">100DaysToOffload</span></a> <a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/there-exists-more-than-rest</guid>
      <pubDate>Mon, 09 Jan 2023 00:07:50 +0000</pubDate>
    </item>
    <item>
      <title>Frontend dev is best dev</title>
      <link>https://stealthycoder.writeas.com/frontend-dev-is-best-dev?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[So this has been the case for many a year now, that everything in JavaScript land or in this day and age, TypeScript land, is targeting the fact that the developer can use one codebase for everything frontend and backend. !--more-- This means you would use the same file for the definitions of the data models that both the NodeJS/Deno backend will use as well as the frontend to query/reason about those objects. Use a simple REST API to ship data back and forth and so on.&#xA;&#xA;I just have one thought/question; why?&#xA;&#xA;Why indeed&#xA;&#xA;Well it seems like more and more was pulled into the frontend as time passed on, mainly to free up the backend to do the real processing. Like number crunching, data processing and so on. Which makes sense, but now it is weird to say the state of an application should be as close to the user as possible. Also servers are now more than capable of doing both work, and therefore the natural conclusion is that since most of the things were in the frontend anyway now; why not move the whole thing to the frontend?&#xA;&#xA;The thing is that it makes the codebase more difficult to manage, you introduce unwanted complexity let alone making it secure. The whole codebase has so many extra dependencies and vulnerabilities since a lot of Server Side Template Injection (SSTI) plague the landscape now and have been fixed/found already in the old backend languages. &#xA;&#xA;Of course it seems convenient, but let&#39;s take the data models that you want to use. You define them either from the frontend perspective or backend perspective first. In the frontend you might only want first name and an e-mail address, for example. Then in the backend you want to have maybe something more than just first name and an email address. How about a password to login? Or a session id, or maybe just an id in general for the databases so you can store multiple combinations of people? Then you have to transform schemas or build them differently. So you get views or something equivalent and suddenly everything is back to this again.&#xA;&#xA;I would steer away from having the whole application in one language with one library/framework since nothing so far checks all the boxes and covers all the bases. &#xA;&#xA;#100DaysToOffload #frontend #JavaScript #devlife&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>So this has been the case for many a year now, that everything in JavaScript land or in this day and age, TypeScript land, is targeting the fact that the developer can use one codebase for everything frontend and backend.  This means you would use the same file for the definitions of the data models that both the NodeJS/Deno backend will use as well as the frontend to query/reason about those objects. Use a simple REST API to ship data back and forth and so on.</p>

<p>I just have one thought/question; why?</p>

<h2 id="why-indeed" id="why-indeed">Why indeed</h2>

<p>Well it seems like more and more was pulled into the frontend as time passed on, mainly to free up the backend to do the real processing. Like number crunching, data processing and so on. Which makes sense, but now it is weird to say the state of an application should be as close to the user as possible. Also servers are now more than capable of doing both work, and therefore the natural conclusion is that since most of the things were in the frontend anyway now; why not move the whole thing to the frontend?</p>

<p>The thing is that it makes the codebase more difficult to manage, you introduce unwanted complexity let alone making it secure. The whole codebase has so many extra dependencies and vulnerabilities since a lot of Server Side Template Injection (SSTI) plague the landscape now and have been fixed/found already in the old backend languages.</p>

<p>Of course it seems convenient, but let&#39;s take the data models that you want to use. You define them either from the frontend perspective or backend perspective first. In the frontend you might only want first name and an e-mail address, for example. Then in the backend you want to have maybe something more than just first name and an email address. How about a password to login? Or a session id, or maybe just an id in general for the databases so you can store multiple combinations of people? Then you have to transform schemas or build them differently. So you get views or something equivalent and suddenly everything is back to this again.</p>

<p>I would steer away from having the whole application in one language with one library/framework since nothing so far checks all the boxes and covers all the bases.</p>

<p><a href="https://stealthycoder.writeas.com/tag:100DaysToOffload" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">100DaysToOffload</span></a> <a href="https://stealthycoder.writeas.com/tag:frontend" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">frontend</span></a> <a href="https://stealthycoder.writeas.com/tag:JavaScript" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">JavaScript</span></a> <a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/frontend-dev-is-best-dev</guid>
      <pubDate>Sun, 08 Jan 2023 23:48:30 +0000</pubDate>
    </item>
    <item>
      <title>File sharing is difficult</title>
      <link>https://stealthycoder.writeas.com/file-sharing-is-difficult?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[You would think this takes place in the early days of computing, but no this takes place in 2023. I needed to share some files to help my brother to launch a new website. !--more-- I told him just get me the files and I will put them online. So you cannot just mail them, since they could be considered harmful/malicious files, or it is too big to send through the mail provider. Then you go to services like WeTransfer but then you have a limit on time to get the files, not only that if you change one file then you have to send it again. It gets very difficult to track what file belongs to what state unless you name them like that. &#xA;&#xA;Then there are other options like running a FTP server yourself to share them, but that is not very intuitive to non-tech people. So you have to use a service of some kind, we settled on Dropbox in this case. Now getting away from just helping out a friend. &#xA;&#xA;How about helping someone in my own household. I have some videos/pictures I want to send to another machine very easily and directly. I could use Bluetooth but it is horrible at file transfer specifically. The rest it does quite nicely. So if I transfer from my PC or device to another I usually just start a python3 -m http.server in the directory holding the files and then downloading the files I need. &#xA;&#xA;Every time I run into this problem I find myself asking why are these seemingly so simple and core functionalities so difficult to implement. Even if everyone would run Windows, it is not guaranteed to work because I have a Windows machine in my network and I cannot get it to share files with any other machine.&#xA;&#xA;Casual on the go sharing&#xA;&#xA;Let us say you want to quickly share a file with a friend you happened to run into. You cannot share it via Bluetooth you will be standing there for 10 minutes. You either have to e-mail it, but then what mail address? You have to send it through some sort of messaging service, but then do you have them in that particular service? &#xA;&#xA;Why can I not just drag and drop a file to this person&#39;s contact information and they will get the file? This should seem very easy to attain for a mobile phone OS. &#xA;&#xA;If there is another way like NFC to share the file instantly that would be wonderful, but I have not found it yet. This is not limited to files though, what about Spotify songs, YouTube videos, webcomics, or anything else digital you can think of. &#xA;&#xA;#100DaysToOffload #devlife]]&gt;</description>
      <content:encoded><![CDATA[<p>You would think this takes place in the early days of computing, but no this takes place in 2023. I needed to share some files to help my brother to launch a new website.  I told him just get me the files and I will put them online. So you cannot just mail them, since they could be considered harmful/malicious files, or it is too big to send through the mail provider. Then you go to services like WeTransfer but then you have a limit on time to get the files, not only that if you change one file then you have to send it again. It gets very difficult to track what file belongs to what state unless you name them like that.</p>

<p>Then there are other options like running a FTP server yourself to share them, but that is not very intuitive to non-tech people. So you have to use a service of some kind, we settled on Dropbox in this case. Now getting away from just helping out a friend.</p>

<p>How about helping someone in my own household. I have some videos/pictures I want to send to another machine very easily and directly. I could use Bluetooth but it is horrible at file transfer specifically. The rest it does quite nicely. So if I transfer from my PC or device to another I usually just start a <code>python3 -m http.server</code> in the directory holding the files and then downloading the files I need.</p>

<p>Every time I run into this problem I find myself asking why are these seemingly so simple and core functionalities so difficult to implement. Even if everyone would run Windows, it is not guaranteed to work because I have a Windows machine in my network and I cannot get it to share files with any other machine.</p>

<h2 id="casual-on-the-go-sharing" id="casual-on-the-go-sharing">Casual on the go sharing</h2>

<p>Let us say you want to quickly share a file with a friend you happened to run into. You cannot share it via Bluetooth you will be standing there for 10 minutes. You either have to e-mail it, but then what mail address? You have to send it through some sort of messaging service, but then do you have them in that particular service?</p>

<p>Why can I not just drag and drop a file to this person&#39;s contact information and they will get the file? This should seem very easy to attain for a mobile phone OS.</p>

<p>If there is another way like NFC to share the file instantly that would be wonderful, but I have not found it yet. This is not limited to files though, what about Spotify songs, YouTube videos, webcomics, or anything else digital you can think of.</p>

<p><a href="https://stealthycoder.writeas.com/tag:100DaysToOffload" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">100DaysToOffload</span></a> <a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/file-sharing-is-difficult</guid>
      <pubDate>Sat, 07 Jan 2023 23:51:08 +0000</pubDate>
    </item>
    <item>
      <title>Investing in the past</title>
      <link>https://stealthycoder.writeas.com/investing-in-the-past?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I quite like keeping old things alive, longer than they were meant for maybe, but also repairing things that got broken. For example toys my children play with. !--more-- Currently I am listening to music through  a setup involving a device my dad bought new in 1983 and I like the fact that he bought whilst being in his 20s and me in my 30s are still using it. I have speakers in the living room that belonged to a woman I have known in my youth that lived to be slightly over 100 years old. These speakers are from Marantz. When I took them to the shop where I bought my vinyl record player, the owner who is an expert in his field said I did not even know Marantz made speakers. They are hooked to an amplifier my parents in law bought in the late 80s. Back to the device my dad bought though.&#xA;&#xA;JVC SEA-33&#xA;&#xA;It is a JVC SEA-33 Graphic Equalizer. This thing was made in the time that everything was analogue, and when mixing was not always done correctly. So what you could do is hook this thing up in your sound flow and change the amplifying or suppressing of certain frequencies to help bring out a better sound experience. Now my laptop has a 3.5mm Jack to two RCA adapter cable, and my headphones has a 6.3mm to two RCA adapter cable to hook everything up. Of course my laptop has a digital soundcard so it is not as effective as it could be, yet still I can tweak things and I just like the fact I have it working. Furthermore I can hook it up again to my record player into my amplifier in the future and then it will all work as it should be. &#xA;&#xA;Kintsugi&#xA;&#xA;This brings me way totally into another area, but we ordered a cake platter and it got delivered shattered. Yeah I know, there is no segue into this. Instead of returning it, we heard of kintsugi which is basically using a golden glue substance to mend broken vases, porcelain- and glassware. I liked this idea and now have a cool set piece which looks much better than the new version we got after reporting this broken delivery to customer service. &#xA;&#xA;Limiting waste&#xA;&#xA;I like the idea of minimizing waste and not just throw out stuff that could be repaired for a smaller fee than buying it new. What I do miss sometimes though is the fact that it is not appreciated by the companies who make their products that you start repairing them instead of buying new stuff. They will make it so difficult and hard to get into stuff or have the parts you can use to repair in a catalog somewhere. &#xA;&#xA;Like the old days when the first PCs came to the market for consumers, everyone got a giant list of part numbers and you could just order more specific parts if you needed them again. Now we actually have to fight for our right to repair stuff we already bought. It should be ours to do with what we want, but that control has long slipped from us consumers. &#xA;&#xA;Knowledge&#xA;&#xA;I also have the idea a certain body of knowledge has been forgotten, or is in the process of being forgotten and that is worrying. Not only because it might set us back in development as a species but also because it might mean we are stuck in a loop or will repeat past mistakes. &#xA;&#xA;Advice&#xA;&#xA;My advice is to just buy old stuff and repurpose it if you feel like it. I still want to buy an old radio from the 60s and put in a Raspberry Pi Zero or equivalent and make it a bluetooth enabled speaker system that can also play YouTube and other songs, yet it still being a wonderful piece to look at. It will be a nice hybrid between analogue and digital, the past being brought into the future. &#xA;&#xA;I have an old PC, 486DX2/50MHz, lying here that will have a running server on the network serving a real site. Obviously behind a TLS offloading proxy but it will still be serving the webpage. I hooked it up to my giant 65&#34; Sony TV and I just loved the fact I held 30 year old tech that still worked and could fulfill a job today. &#xA;&#xA;I also operated a saw that belonged to my great grand father, who I&#39;ve met, and I learned that it might have been my great great grand father&#39;s even. That tool easily has been in my family&#39;s hands for more than a 100 years, and I intend to further that lifespan. It is wonderful that still exists. It works beautiful and you cannot buy a similar product anymore these days. &#xA;&#xA;There something beautiful in that old things keep finding purposes and trying to get old things to fulfill a new purpose is fun to do. I do not think my dad in his 20s ever thought my son in his 30s will use this. Who knows what kind of tech I will buy that my children will repurpose in the future. &#xA;&#xA;#100DaysToOffload #devlife&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>I quite like keeping old things alive, longer than they were meant for maybe, but also repairing things that got broken. For example toys my children play with.  Currently I am listening to music through  a setup involving a device my dad bought new in 1983 and I like the fact that he bought whilst being in his 20s and me in my 30s are still using it. I have speakers in the living room that belonged to a woman I have known in my youth that lived to be slightly over 100 years old. These speakers are from Marantz. When I took them to the shop where I bought my vinyl record player, the owner who is an expert in his field said I did not even know Marantz made speakers. They are hooked to an amplifier my parents in law bought in the late 80s. Back to the device my dad bought though.</p>

<h2 id="jvc-sea-33" id="jvc-sea-33">JVC SEA-33</h2>

<p>It is a JVC SEA-33 Graphic Equalizer. This thing was made in the time that everything was analogue, and when mixing was not always done correctly. So what you could do is hook this thing up in your sound flow and change the amplifying or suppressing of certain frequencies to help bring out a better sound experience. Now my laptop has a 3.5mm Jack to two RCA adapter cable, and my headphones has a 6.3mm to two RCA adapter cable to hook everything up. Of course my laptop has a digital soundcard so it is not as effective as it could be, yet still I can tweak things and I just like the fact I have it working. Furthermore I can hook it up again to my record player into my amplifier in the future and then it will all work as it should be.</p>

<h2 id="kintsugi" id="kintsugi">Kintsugi</h2>

<p>This brings me way totally into another area, but we ordered a cake platter and it got delivered shattered. Yeah I know, there is no segue into this. Instead of returning it, we heard of <em>kintsugi</em> which is basically using a golden glue substance to mend broken vases, porcelain- and glassware. I liked this idea and now have a cool set piece which looks much better than the new version we got after reporting this broken delivery to customer service.</p>

<h2 id="limiting-waste" id="limiting-waste">Limiting waste</h2>

<p>I like the idea of minimizing waste and not just throw out stuff that could be repaired for a smaller fee than buying it new. What I do miss sometimes though is the fact that it is not appreciated by the companies who make their products that you start repairing them instead of buying new stuff. They will make it so difficult and hard to get into stuff or have the parts you can use to repair in a catalog somewhere.</p>

<p>Like the old days when the first PCs came to the market for consumers, everyone got a giant list of part numbers and you could just order more specific parts if you needed them again. Now we actually have to fight for our <strong>right</strong> to repair stuff we already bought. It should be ours to do with what we want, but that control has long slipped from us consumers.</p>

<h2 id="knowledge" id="knowledge">Knowledge</h2>

<p>I also have the idea a certain body of knowledge has been forgotten, or is in the process of being forgotten and that is worrying. Not only because it might set us back in development as a species but also because it might mean we are stuck in a loop or will repeat past mistakes.</p>

<h2 id="advice" id="advice">Advice</h2>

<p>My advice is to just buy old stuff and repurpose it if you feel like it. I still want to buy an old radio from the 60s and put in a Raspberry Pi Zero or equivalent and make it a bluetooth enabled speaker system that can also play YouTube and other songs, yet it still being a wonderful piece to look at. It will be a nice hybrid between analogue and digital, the past being brought into the future.</p>

<p>I have an old PC, 486DX2/50MHz, lying here that will have a running server on the network serving a real site. Obviously behind a TLS offloading proxy but it will still be serving the webpage. I hooked it up to my giant 65” Sony TV and I just loved the fact I held 30 year old tech that still worked and could fulfill a job today.</p>

<p>I also operated a saw that belonged to my great grand father, who I&#39;ve met, and I learned that it might have been my great great grand father&#39;s even. That tool easily has been in my family&#39;s hands for more than a 100 years, and I intend to further that lifespan. It is wonderful that still exists. It works beautiful and you cannot buy a similar product anymore these days.</p>

<p>There something beautiful in that old things keep finding purposes and trying to get old things to fulfill a new purpose is fun to do. I do not think my dad in his 20s ever thought my son in his 30s will use this. Who knows what kind of tech I will buy that my children will repurpose in the future.</p>

<p><a href="https://stealthycoder.writeas.com/tag:100DaysToOffload" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">100DaysToOffload</span></a> <a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/investing-in-the-past</guid>
      <pubDate>Thu, 05 Jan 2023 21:39:29 +0000</pubDate>
    </item>
    <item>
      <title>But can I write to it?</title>
      <link>https://stealthycoder.writeas.com/but-can-i-write-to-it?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I just wanted to find out if a directory was writable for the user, and it turns out it is quite difficult to get that information in Golang. How difficult could it be? !--more--&#xA;&#xA;Stat has that information&#xA;&#xA;So my first inclination was that the os.Stat call has that information. It sort of does, in a way, but only for Linux. There is a Sys() method on the fs.FileInfo which returns a specific struct on Linux. &#xA;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;os&#34;&#xA;&#x9;&#34;syscall&#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;info, err := os.Stat(&#34;/tmp/some.file&#34;)&#xA;&#x9;if err != nil {&#xA;&#x9;&#x9;fmt.Printf(&#34;%s&#34;, err)&#xA;&#x9;}&#xA;&#x9;if data, ok := info.Sys().(*syscall.Statt); ok {&#xA;&#x9;&#x9;fmt.Printf(&#34;UID: %d\n&#34;, data.Uid)&#xA;&#x9;}&#xA;}&#xA;Is an example of how you get to that part and if you call syscall.Getuid() you can check it if they are the same and therefore if you at least own the resource. However that does not mean it is writable yet. &#xA;&#xA;Permission bit logic&#xA;&#xA;I tried to finagle some bitwise logic with the permission bits, but again they only work on Linux and truth be told I never trusted myself that I got it to work. &#xA;&#xA;Sidetrack to Java&#xA;&#xA;So in Java there has been this thing) since forever. You give it a path, and it tells you if it is writable. It is a static method and easy to use. Why does this not exist in Golang?!?!?&#xA;&#xA;Solution&#xA;&#xA;So I did finally create a solution that was tailored for Unix and Windows separately. &#xA;&#xA;Windows&#xA;&#xA;The Windows solution made me go down a rabbit hole, read up on Win32 API structs and methods on the Microsoft docs and dig deep down in the source of Go itself to figure out what I have access to. I will just show you the code:&#xA;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;syscall&#34;&#xA;)&#xA;&#xA;// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants&#xA;const FILEAPPENDFILE = 0x00000002&#xA;&#xA;// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters at dwShareMode&#xA;const FILELOCK = 0x00000000&#xA;&#xA;func main() {&#xA;&#x9;// Checks if directory is writable&#xA;&#x9;if hwnd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(&#34;C:\\Windows\\system32&#34;), FILEAPPENDFILE, FILELOCK, nil, syscall.OPENEXISTING, syscall.FILEFLAGBACKUPSEMANTICS|syscall.FILEFLAGOPENREPARSEPOINT, 0); err == nil {&#xA;&#x9;&#x9;if err = syscall.CloseHandle(hwnd); err != nil {&#xA;&#x9;&#x9;&#x9;fmt.Printf(&#34;%s\n&#34;, err)&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;fmt.Printf(&#34;This directory is writable&#34;)&#xA;&#x9;}&#xA;}&#xA;So yeah, a syscall to CreateFile to open a handle to a directory. I cannot figure out why you need to create a file to get a handle to be able to tell if you can write to a directory, even more so because CreateDirectoryW is also an actual call in the Win32 API, which is the only one that can actually create a directory. This is so confusing. &#xA;&#xA;Linux / MacOSX&#xA;&#xA;The other solution was sort of similar but much easier. There is a nice syscall to Access. &#xA;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;syscall&#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;// Checks if directory is writable&#xA;&#x9;if err := syscall.Access(&#34;/opt/&#34;, syscall.ORDWR); err == nil {&#xA;&#x9;&#x9;fmt.Printf(&#34;This directory is writable&#34;)&#xA;&#x9;}&#xA;}&#xA;&#xA;Conclusion&#xA;&#xA;I feel like all of this code can be hidden away in the Golang standard library and give us a nice os.IsWritable(path string) bool function signature for it. &#xA;&#xA;#100DaysToOffload #devlife #golang]]&gt;</description>
      <content:encoded><![CDATA[<p>I just wanted to find out if a directory was writable for the user, and it turns out it is quite difficult to get that information in Golang. How difficult could it be? </p>

<h2 id="stat-has-that-information" id="stat-has-that-information">Stat has that information</h2>

<p>So my first inclination was that the <code>os.Stat</code> call has that information. It sort of does, in a way, but only for Linux. There is a <code>Sys()</code> method on the <code>fs.FileInfo</code> which returns a specific struct on Linux.</p>

<pre><code class="language-golang">package main

import (
	&#34;fmt&#34;
	&#34;os&#34;
	&#34;syscall&#34;
)

func main() {
	info, err := os.Stat(&#34;/tmp/some.file&#34;)
	if err != nil {
		fmt.Printf(&#34;%s&#34;, err)
	}
	if data, ok := info.Sys().(*syscall.Stat_t); ok {
		fmt.Printf(&#34;UID: %d\n&#34;, data.Uid)
	}
}
</code></pre>

<p>Is an example of how you get to that part and if you call <code>syscall.Getuid()</code> you can check it if they are the same and therefore if you at least own the resource. However that does not mean it is writable yet.</p>

<h2 id="permission-bit-logic" id="permission-bit-logic">Permission bit logic</h2>

<p>I tried to finagle some bitwise logic with the permission bits, but again they only work on Linux and truth be told I never trusted myself that I got it to work.</p>

<h2 id="sidetrack-to-java" id="sidetrack-to-java">Sidetrack to Java</h2>

<p>So in Java there has been this <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#isWritable(java.nio.file.Path)" rel="nofollow">thing</a> since forever. You give it a path, and it tells you if it is writable. It is a static method and easy to use. Why does this not exist in Golang?!?!?</p>

<h2 id="solution" id="solution">Solution</h2>

<p>So I did finally create a solution that was tailored for Unix and Windows separately.</p>

<h3 id="windows" id="windows">Windows</h3>

<p>The Windows solution made me go down a rabbit hole, read up on Win32 API structs and methods on the Microsoft docs and dig deep down in the source of Go itself to figure out what I have access to. I will just show you the code:</p>

<pre><code class="language-golang">package main

import (
	&#34;fmt&#34;
	&#34;syscall&#34;
)

// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
const FILE_APPEND_FILE = 0x00000002

// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters at dwShareMode
const FILE_LOCK = 0x00000000

func main() {
	// Checks if directory is writable
	if hwnd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(&#34;C:\\Windows\\system32&#34;), FILE_APPEND_FILE, FILE_LOCK, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0); err == nil {
		if err = syscall.CloseHandle(hwnd); err != nil {
			fmt.Printf(&#34;%s\n&#34;, err)
		}
		fmt.Printf(&#34;This directory is writable&#34;)
	}
}
</code></pre>

<p>So yeah, a syscall to <code>CreateFile</code> to open a handle to a directory. I cannot figure out why you need to create a file to get a handle to be able to tell if you can write to a directory, even more so because <code>CreateDirectoryW</code> is also an actual call in the Win32 API, which is the only one that can actually create a directory. This is so confusing.</p>

<h3 id="linux-macosx" id="linux-macosx">Linux / MacOSX</h3>

<p>The other solution was sort of similar but much easier. There is a nice syscall to Access.</p>

<pre><code class="language-golang">package main

import (
	&#34;fmt&#34;
	&#34;syscall&#34;
)

func main() {
	// Checks if directory is writable
	if err := syscall.Access(&#34;/opt/&#34;, syscall.O_RDWR); err == nil {
		fmt.Printf(&#34;This directory is writable&#34;)
	}
}
</code></pre>

<h1 id="conclusion" id="conclusion">Conclusion</h1>

<p>I feel like all of this code can be hidden away in the Golang standard library and give us a nice <code>os.IsWritable(path string) bool</code> function signature for it.</p>

<p><a href="https://stealthycoder.writeas.com/tag:100DaysToOffload" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">100DaysToOffload</span></a> <a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a> <a href="https://stealthycoder.writeas.com/tag:golang" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">golang</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/but-can-i-write-to-it</guid>
      <pubDate>Wed, 04 Jan 2023 21:56:54 +0000</pubDate>
    </item>
    <item>
      <title>Chameleon containers</title>
      <link>https://stealthycoder.writeas.com/chameleon-containers?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Sometimes you need to run Docker containers in different circumstances, like on Raspberry Pi&#39;s or you have systems that are just wired differently (Alpine) and sometimes even a combination of those (Alpine on a BananaPi) and you do not always have everything laying around to reproduce it. So what do you do? !--more--&#xA;&#xA;I ran into this problem because someone was getting a new Apple MacBook with the M1 chip which is ARMv8 or aarch64 and therefore all my previously made Docker images did not work anymore for this person as they did not work for his architecture. &#xA;&#xA;Luckily Alpine and Debian also provide images for the ARMv8 architecture. Yet that meant I had to build two different images and have a multi arch manifest file inside AWS ECR. Which they support and is easy to do, but I did not want to have that plus it made it so difficult to try and reproduce things as I could build cross architecture but not run it yet. &#xA;&#xA;Get yourself QEMU&#xA;&#xA;So the following one line is all you need to make it possible to run as different architectures.&#xA;&#xA;sudo docker run --rm --privileged multiarch/qemu-user-static:register&#xA;&#xA;That will register all the necessary binaries in order to simulate the other architectures. Then for example run the following:&#xA;&#xA;docker run -it --rm multiarch/alpine:aarch64-edge /bin/ash&#xA;&#xA;Then you get an aarch64 image!.&#xA;&#xA;  Verify with uname -a and arch&#xA;&#xA;Now you can see what it is like to run on those other architectures. The binaries are all of the form qemu-ARCH-static and so there is one for MIPS, SPARC and even PowerPC. &#xA;&#xA;#devlife #devops&#xA;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Sometimes you need to run Docker containers in different circumstances, like on Raspberry Pi&#39;s or you have systems that are just wired differently (Alpine) and sometimes even a combination of those (Alpine on a BananaPi) and you do not always have everything laying around to reproduce it. So what do you do? </p>

<p>I ran into this problem because someone was getting a new Apple MacBook with the M1 chip which is ARMv8 or <code>aarch64</code> and therefore all my previously made Docker images did not work anymore for this person as they did not work for his architecture.</p>

<p>Luckily Alpine and Debian also provide images for the ARMv8 architecture. Yet that meant I had to build two different images and have a multi arch manifest file inside AWS ECR. Which they support and is easy to do, but I did not want to have that plus it made it so difficult to try and reproduce things as I could build cross architecture but not run it yet.</p>

<h1 id="get-yourself-qemu" id="get-yourself-qemu">Get yourself QEMU</h1>

<p>So the following one line is all you need to make it possible to run as different architectures.</p>

<pre><code>sudo docker run --rm --privileged multiarch/qemu-user-static:register
</code></pre>

<p>That will register all the necessary binaries in order to simulate the other architectures. Then for example run the following:</p>

<pre><code>docker run -it --rm multiarch/alpine:aarch64-edge /bin/ash
</code></pre>

<p>Then you get an <code>aarch64</code> image!.</p>

<blockquote><p>Verify with <code>uname -a</code> and <code>arch</code></p></blockquote>

<p>Now you can see what it is like to run on those other architectures. The binaries are all of the form <code>qemu-&lt;ARCH&gt;-static</code> and so there is one for MIPS, SPARC and even PowerPC.</p>

<p><a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a> <a href="https://stealthycoder.writeas.com/tag:devops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devops</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/chameleon-containers</guid>
      <pubDate>Fri, 27 Aug 2021 14:59:35 +0000</pubDate>
    </item>
  </channel>
</rss>