<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by David Ohana on Medium]]></title>
        <description><![CDATA[Stories by David Ohana on Medium]]></description>
        <link>https://medium.com/@davidoha?source=rss-418b440883c6------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*huzZYgPCHsdlMFMW</url>
            <title>Stories by David Ohana on Medium</title>
            <link>https://medium.com/@davidoha?source=rss-418b440883c6------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 05 Apr 2026 11:59:41 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@davidoha/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[On the Safe Side: Log your 3rd-party Package Versions]]></title>
            <link>https://itnext.io/on-the-safe-side-log-your-3rd-party-package-versions-8b70ebdc9e1d?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/8b70ebdc9e1d</guid>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[maven]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[version]]></category>
            <category><![CDATA[security]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Wed, 22 Dec 2021 15:50:16 GMT</pubDate>
            <atom:updated>2022-01-09T14:06:22.140Z</atom:updated>
            <content:encoded><![CDATA[<h3>Better safe than worry: log the versions of all dependencies at runtime</h3><p><em>In short: Tutorial for how to easily get the versions of log4j2 and the rest of your Java dependencies </em><strong><em>at runtime</em></strong><em>.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*V26PTWtprt5grsu3.jpg" /></figure><p>In the light of the recent <a href="https://logging.apache.org/log4j/2.x/security.html">log4j2 vulnerability</a>, it happens frequently that you have to upgrade a version of some 3rd-party dependency in order to mitigate a recently discovered exploit.</p><p>But how to be sure that the version update actually happened? With nowadays complex dependency rules, even when you specify an explicit package version somewhere, your package manager of choice (e.g. Maven, Gradle) might decide to set your package version to some lower version, to adhere to some common dependency with another package you use.</p><h3>Listing your dependencies at build time</h3><p>If you use Maven, you can runmvn dependency:resolve and the maven-dependency-plugin will list your dependencies. But this applies only at build-time.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/928f9b409a4f58d848ce3fb0896d59c4/href">https://medium.com/media/928f9b409a4f58d848ce3fb0896d59c4/href</a></iframe><p>It is recommended to add this command to your build workflow so that all dependencies are clearly logged already at build time.</p><h3>Listing your dependencies at run time</h3><p>It’s a good practice for your application to print/log the versions of all <strong>loaded</strong> packages <strong>at</strong> <strong>run-time as well </strong>so that you can always look at logs of the production environment and be sure that the proper version is there.</p><h4>Kotlin/Java Implementaation</h4><p>It is possible to use reflection to get all loaded packages. Then, for each package — read and print implementation metadata from its JAR manifest. Here is a sample Kotlin code that does this. In the demo, I excluded built-in Java packages.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ee4f56f72e5ae64eed927db039666c6a/href">https://medium.com/media/ee4f56f72e5ae64eed927db039666c6a/href</a></iframe><p>This is the output:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/414ce53ffb8f46a55c2a6f51d2f21275/href">https://medium.com/media/414ce53ffb8f46a55c2a6f51d2f21275/href</a></iframe><p><strong>An important note</strong>: The code above prints only packages that are loaded to the JVM at the time of the call. So, you want to call it <strong>not</strong> at the very beginning of your app, but only after 3rd party components are already initialized or started.</p><h4>Better Formatting</h4><p>Printing all loaded package versions can be too verbose as you may have hundreds of loaded packages. I also prepared another flavor, which groups all packages by their product (title) and version. It also lets you decide whether to print all the packages in each product.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7dfe8d68d878d349bf4c5d3f20f843a0/href">https://medium.com/media/7dfe8d68d878d349bf4c5d3f20f843a0/href</a></iframe><p>Output — no details:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0c481a096c38cc55a94039be2df236f3/href">https://medium.com/media/0c481a096c38cc55a94039be2df236f3/href</a></iframe><p>Output — with details (clipped):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/88a164c55947ec5a45f48828e4cfb6fe/href">https://medium.com/media/88a164c55947ec5a45f48828e4cfb6fe/href</a></iframe><h4>Preserving Manifests of Dependencies</h4><p>Since the approach above relies on JAR manifest files, it will not work properly if you pack your application into a a single JAR file with maven-assembly-plugin(“big-jar”). Instead, you will get null values for all implementation* fields. That is because each JAR can have a single manifest file, and the original manifests of each dependency are omitted in the packing process.<br>Workaround: Instead of producing a big jar, use the maven-dependency-plugin to copy JARs of your dependencies to a lib folder below your app-only JAR file. In addition, use maven-jar-plugin to add lib folder to the classpath, as demonstrated below.<br>If you have a big-jar solution, I would be happy to know about it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b5c890a88ad8ec3b4189bd698c30204e/href">https://medium.com/media/b5c890a88ad8ec3b4189bd698c30204e/href</a></iframe><p>It is also recommended to set the &lt;addDefaultImplementationEntries&gt;true&lt;/addDefaultImplementationEntries&gt; directive as shown above for maven-jar-plugin. This will update the Implementation-* fields in the MANIFEST.MF file in your own JAR from the pom.xml file — so that your own packages will be properly versioned as well.</p><h4>Simpler Solution</h4><p>But wait — If we already copy each dependency JAR below our app.jar using maven-dependency-plugin, there is a much simpler solution! Just log the filenames of the .jar files. When using a dependency manager, those already contain the version number. This also has other advantages over the previous method:</p><ul><li>It logs the version off any JAR, regardless of if it&#39;s loaded.</li><li>Many authors don’t bother to set the implementation fields in their JAR manifest, but every library in MavenCentral has a file version, that is preserved in the directory structure properly.</li></ul><p>Here is the implementation:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/70718d03eeebcda2b5b1c34161a745cb/href">https://medium.com/media/70718d03eeebcda2b5b1c34161a745cb/href</a></iframe><p>The output (clipped):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7e5bc1fb97df774be573f33a6062da25/href">https://medium.com/media/7e5bc1fb97df774be573f33a6062da25/href</a></iframe><h4>Summary</h4><p>Regardless of your language, it&#39;s a good practice to log versions of all your dependencies to be sure known vulnerabilities are mitigated properly. I demonstrated a few implementation alternatives in Kotlin. The first approach is based on printing the version of packages loaded into the JVM. Using it you can be absolutely sure what version of each package is in use. However, it is not as robust as the latter approach of simply copying dependency JARs to a known location and printing their filenames.</p><p>So don’t wait for the next vulnerability to be discovered! Add dependency version logging to your bootstrapping code now, and you will be able to determine instantly if mitigation is required.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8b70ebdc9e1d" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/on-the-safe-side-log-your-3rd-party-package-versions-8b70ebdc9e1d">On the Safe Side: Log your 3rd-party Package Versions</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[10 Tips for Machine Learning Experiment Tracking and Reproducibility — Do It Yourself Approach…]]></title>
            <link>https://davidoha.medium.com/10-tips-for-machine-learning-experiment-tracking-and-reproducibility-do-it-yourself-approach-f7c31c533d94?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/f7c31c533d94</guid>
            <category><![CDATA[experiment]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[research]]></category>
            <category><![CDATA[reproducibility]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Sun, 21 Nov 2021 08:48:37 GMT</pubDate>
            <atom:updated>2021-12-09T13:26:46.808Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>10 “Do It Yourself” Tips for Machine Learning Experiment Tracking and Reproducibility</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4xA37_vVejD1_2mGasGeDA.jpeg" /><figcaption>Image credit: <a href="https://pixabay.com/users/wiredsmartio-14931632/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=5090797">wiredsmartio/pixabay</a></figcaption></figure><p>This post was also published in the <a href="https://developer.ibm.com/blogs/10-diy-tips-for-machine-learning-experiment-tracking-and-reproducibility">IBM Developer Blog</a>.</p><p>As machine-learning practitioners, we invest significant time and effort to improve our models. You usually do it iteratively and experimentally, by repeatedly changing your model, running an experiment, and examining the results, deciding whether the recent model change was positive and should be kept or discarded.</p><p>Changes in each iteration may involve, for example, changing a value for a hyper-parameter, adding a new input feature, changing the underlying ML model (e.g., using Gradient Boosting Classifier instead of Random Forest classifier), trying a new heuristic, or trying an entirely new approach.</p><p>Experimentation cycles can cause a great deal of confusion. It is easy to get lost, forgetting what changes you made in the recent experiments and whether the latest results are indeed better than before. A single experiment can take hours or even longer to complete. So, you try to optimize your time and execute multiple experiments simultaneously. This makes it even less manageable and the confusion gets even worse.</p><p>In this article, I will share lessons and good practices I learned in my recent machine learning projects. Although I call it a “Do it yourself” approach, some may call it “<a href="https://hadyelsahar.medium.com/how-do-you-manage-your-machine-learning-experiments-ab87508348ac">The caveman way</a>”. I am fully aware that nowadays there are many experiment tracking and management platforms, but it is not always possible or convenient to use them. Some platforms require that you execute your experiments on their platform. Sometimes you can’t share any sensitive information outside of your organization — not just the datasets but also results and code. Many platforms require a paid subscription, which can also be a problem in some cases. Sometimes you just want full control of your experiment management approach and data.</p><p>The practices described below are easy to implement and do not require additional tooling. They are mostly suitable for small to medium ML projects with a single researcher or a small team. Most of the artifacts are saved locally and adaptations may be required if you want to use shared storage. As a seasoned developer of production systems, I’m aware that a few of the tips below might be considered ‘code-smells’ or bad practices when it comes to the traditional development approach of such systems. However, I believe they have their place and are justified for short-term research projects. I would like to emphasize that the tips below reflect my personal journey and point of view, and not necessarily any official views or practices. So, here I am, waiting for your stones :-)</p><h3><strong>Tracking what you did</strong></h3><h4><strong>1. Use source control</strong></h4><p>It goes without saying that your experimentation code should be source-controlled. That said, when using modern interactive environments like Jupyter Notebooks, it is easy to be tempted to make quick experiments on the fly without committing changes to GIT or any other source control system. Try to avoid that as much as possible. Maybe it is only me, but I prefer using a decent IDE and plain Python scripts to run experiments. I may use a notebook for the initial data exploration, but soon after an initial model skeleton is ready, I switch to a full-fledged Python script, which also allows debugging, refactoring, etc.</p><h4>2. <strong>Use identifiable experiments</strong></h4><p>But you know what? Source control isn’t enough. Even if everything is source-controlled, it can be tedious to browse the repository’s history and understand what source was used for running some experiment 12 days ago. I would like to suggest an additional practice that I call “Copy on Write”. Duplicate your latest experiment script file/folder before each new experiment and make the changes on the new file. Make your experiments identifiable by adding a sequential number to each experiment in the source file name. For example, <em>animal_classifier_009.py</em> for experiment #9. And yes, this works also for notebooks: you can create a notebook per experiment. This means you need only a file diff to understand what changed between experiment #9 and #12. Storage is cheap and the size of all of your experiments’ source code is probably dwarfed by the size of your data.</p><h4>3. <strong>Automatic source code snapshots</strong></h4><p>Another tip is to automatically take a snapshot of your experiment code for each run. You can do this easily inside the experiment script itself, by bootstrapping code that copies the source file/folder to a directory with the experiment start timestamp in its name. This will make your experiment tracking strategy robust even if you were tempted to make on-the-fly experiments without committing or copy-on-write above (a.k.a “Dirty Commits”). <br>For example, when running the experiment a<em>nimal_classifier_009.py</em>, we create the folder <em>out/animal_classifier_009/2021_11_03–12_34_12/source</em> and store a snapshot of the relevant source code inside.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/363/1*qZFyjq103SY6ye3z5jv0sA.png" /><figcaption>Source code snapshot on disk</figcaption></figure><h4>4. <strong>Treat configuration parameters same as source code</strong></h4><p>Avoid tuning experiment parameters or hyper-parameters in the command line, environment variables, or any other external means that are not part of the source code. Otherwise, you risk losing traceability for changes if you forget to log the parameter values.</p><p>To embed experiment configuration, you can use either plain Python, dictionary, json, yaml, or any other format you find convenient. Just make sure you commit the configuration files together with the experiment code. Does hard-coding stuff seem like a code smell? Well, not in this case. If you do accept external run-time parameters — be sure to log their values!</p><p>Each configuration changeset should be treated as a unique experiment. It should be committed to source control, configuration files shall be included in the experiment code snapshot, and it should get its own experiment ID. <br> <br>The advantage of embedding configuration as part of the source control is that you can be sure you reproduced the same experiment just by running the program file, not other moving parts that you may forget to set.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*9nACJU4qT_2GAaim-_vHMw.png" /><figcaption>Using plain Python variables for configuration tracking</figcaption></figure><h4>5. <strong>Track experiment evolution tree</strong></h4><p>One of the things that helps me a lot is to keep track of the reference experiment — the predecessor baseline that I am trying to improve upon. This is easy to do if your experiments are identifiable. When you create a new experiment by duplicating, keep track of the parent experiment ID + the essence of what you’ve tried in this experiment that is different from the parent. This information will help you quickly recall what you did, without relying on code diffs. It also makes it possible to traverse back in the experiment tree and quickly get the full picture. You can track the parent experiment inside the source code itself, as a code comment.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/468/1*OYHp6ONi2Zu_9lyIwmiC2w.png" /><figcaption>Experiment notes in code comments</figcaption></figure><p>However, this might cause a problem if you forget to update the notes before running the experiment. I suggest a simple spreadsheet like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/647/1*PX_nV3mnaLUaRZbVyXNCWw.png" /><figcaption>Experiment tracking in a spreadsheet</figcaption></figure><p>In the sheet, you can also capture other information or parameters that you used in this experiment, and of course — experiment results. But we’ll touch on that later.</p><h3>Tracking what happened</h3><h4>6. <strong>Keep console/log output</strong></h4><p>Be generous with logging statements that track what happened in the experiment. Track many metrics and types of information, like dataset size, label count, date ranges, experiment execution time, and more. These can help you detect issues and mistakes. <em>Be paranoid!</em> Every unexplained change in a metric could be caused by some mistake in the experiment setup. This will help you understand its root cause.</p><p>Any experiment output should be persisted. I recommend using the Python <a href="https://realpython.com/python-logging/">logging</a> module instead of plain console prints, so you can redirect logging messages to both stdout and a file. In addition, you will get timestamps for each log event, which can help you to solve performance bottlenecks. You can store the log file under a folder correlated to the experiment ID and execution time:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/359/1*vXKA6AjQRU6V5yY15CRwUQ.png" /><figcaption>Experiment log on disk</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lDoBKiwGGKUMnZIcgjwP_Q.png" /><figcaption>Experiment log output</figcaption></figure><h4>7. <strong>Track experiment results</strong></h4><p>You might use multiple metrics that quantify the quality of your model. For example, accuracy, precision, recall, F-score, AUC. Make sure you track these in a separate, structured, results file that you may automatically process later to show charts, etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/367/1*k8bnly16TSqDd-rZvp6GbA.png" /><figcaption>Experiment results on disk</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/263/1*4SDl8PU79uvkevYpgBlwhg.png" /><figcaption>result.json — structured results file.</figcaption></figure><p>It’s also a good idea to track your most important metrics in the experiment spreadsheet so you can get the full picture quickly and decide on future directions. I like using colors to mark results (green=improved, red=got worse, yellow=not sure).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/933/1*D-0Zh0FpGOL2iy6eVxQxDw.png" /><figcaption>Tracking experiment results in a spreadsheet</figcaption></figure><h4>8. <strong>Do multiple repeats for stochastic models</strong></h4><p>You want your results to be reproducible, but still, avoid getting misleading results due to chance. The solution lies in repetition with random seeds. Avoid using fixed <a href="https://newbedev.com/what-is-random-state-in-sklearn-model-selection-train-test-split-example">random seeds</a> if your models are stochastic. The same applies when shuffling, downsampling, or any operation that contains a random element. If you use SciKitLearn, for example, always run your models with <em>random_state=none</em>. Perform <a href="https://machinelearningmastery.com/estimate-number-experiment-repeats-stochastic-machine-learning-algorithms/">multiple repeats</a> in each experiment and average the results of your optimization target metrics in all repeats, so you get stable numbers. You can use metrics like <em>Standard Error of the Mean</em> (<a href="https://www.investopedia.com/ask/answers/042415/what-difference-between-standard-error-means-and-standard-deviation.asp">SEM</a>), to estimate how close your repeats’ mean is to the true mean of the population (if you could run an infinite number of repeats). SEM metric value decrease as you increase the number of repeats. This will help you gain confidence and understand if your latest results are indeed better, or might it was just luck, and you should increase the repeat count to be sure. In general, when your model gets more mature/stable, your optimizations will probably have a smaller impact and you might need to increase the repeat count.</p><h3>Tracking input and intermediate datasets</h3><h4>9. <strong>Track input datasets</strong></h4><p>Remember to version and name the datasets that are used as input to your model with the version identifier. Input datasets tend to be big, so I wouldn’t recommend duplicating them into each experiment’s tracking folder. Just make sure to log the filenames/URIs of the input datasets you used. You can also find these filenames in the source code snapshots for the relevant experiment. You can add another safety layer here by computing and logging a hash/digest of the contents of each input dataset. Log also the basic characteristics of the data, such as its dimensions and sample counts for each class.</p><h4>10. <strong>Avoid or track intermediate datasets</strong></h4><p>Some of your code may carry out heavy preprocessing of datasets. This can sometimes take a long time, so you may do it once and then use the output in later steps. If your preprocessing has a stochastic nature, (shuffling, train/test splitting, etc..), try to avoid creating intermediate datasets unless the processing can really save a lot of experiment time. Otherwise, you may have an inherent bias in your data, similar to when using a fixed seed. Instead, you can invest in optimizing the execution time of the preprocessing steps. <br>If you do generate intermediate datasets, treat the source code you wrote for that purpose just like a normal experiment using the practices described so far. Use version numbers for the source file, track the source code, track the logs, etc. It’s a good idea to save the output intermediate datasets in the out folder of each experiment. This will make the datasets inherently identifiable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/371/1*TM2Y74R8p_Ovgi11qPzquw.png" /><figcaption>Tracking intermediate datasets</figcaption></figure><h4>Summary</h4><p>In short, experiment management is essential and pretty easy to do if you adopt some simple techniques. No matter whether you do it yourself or use an experiment management platform, just do it!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f7c31c533d94" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Avoiding Bash frustration — Use Python for shell scripts]]></title>
            <link>https://medium.com/swlh/avoiding-bash-frustration-use-python-for-shell-scripts-44bba8ba1e9e?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/44bba8ba1e9e</guid>
            <category><![CDATA[bash]]></category>
            <category><![CDATA[shell-script]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[shell]]></category>
            <category><![CDATA[os]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Sun, 08 Nov 2020 15:37:27 GMT</pubDate>
            <atom:updated>2020-11-10T11:13:53.494Z</atom:updated>
            <content:encoded><![CDATA[<h3>Avoiding Bash frustration — Use Python for shell scripting</h3><p>I never got used to Bash programming syntax. Whenever I have to write a more-than-trivial bash script, the strange syntax annoys me, and I have to Google every little thing I need to do, starting from how to do comparisons in if statements, how to use sed , etc.</p><p>For me, using Python as a shell scripting language seems like a better choice. <br>Python is a more expressive language. It is relatively concise. It has a massive built-in library that let you perform many tasks without even using shell commands, it is cross-platform and it is preinstalled or easily installed in many OS’s. <br>I am aware that some other dynamic languages (e.g Perl, Lua) might also be very suitable for shell programming, but I (and my team) work with Python daily and familiar with it, and it gets the jobe done.</p><p>In my last project, after some bash frustration, I decided to refactor a bloated set of bash scripts with a CLI-style Python script. A side-result of this work is a small, single helper file, which I am going to share with you here, with few utilities that bridge the gap to easily let you use Python for shell scripting.</p><p>In an effort to make this utility runnable ubiquitously, I made it compatible with both Python 2.7 and Python 3.5+ (Python 2.7 is preinstalled in Ubuntu since version 14). There are no 3rd-party requirements — only Python’s standard library is used, so no pip install is required. <br>I tested it on Mac and Ubuntu.</p><p>For shell programming, we need to be able to execute shell commands conveniently. The most extensive Python function for that is subprocess.Popen() . However, Popen() might feel too raw to use easily. It also has some compatibility changes between Python 2/3 and some missing features in Python2.</p><p>The core function I provide here is sh() , which is a friendly wrapper around subprocess.Popen(). It let you execute a shell call from Python, deciding whether to:</p><ul><li>Capture stdout/stderr to a string or print it.</li><li>Time-out the call after x seconds</li><li>Terminate the calling Python script on failure of the shell call.</li><li>Terminate the calling Python script on timeout of the shell call.</li><li>Echo the command string of the shell call.</li><li>Run the command inside a shell [ like subprocess.Popen(shell=true) ]. This is considered an <a href="https://docs.python.org/2/library/subprocess.html#frequently-used-arguments"><strong>insecure</strong></a> practice due to the possibility of a shell injection, but allows many convenient features in shell calls, like pipes (|), environment variables interpolation, executing multiple statements with &amp;&amp; or ; at once call and more, so if your script gets no user input, or you trust your input, you may opt to use it.</li><li>Apply Pythonic formatting arguments to the shell command before executing it.</li><li>And more …</li></ul><p>Other utilities let you:</p><ul><li>Log/print to stdout with ANSI colors according to the logging level.</li><li>Get user input prompt from stdin, with compatibility to both Python 2.7 and Python 3+.</li></ul><p>An example script which:</p><ul><li>Asks the user whether to pull for a new docker image</li><li>Removes the running container for this image (if any)</li><li>Runs a new container for the image.</li><li>Outputs the first 5 seconds of the new container log.</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/612c618cc729403ecab361ca24709e93/href">https://medium.com/media/612c618cc729403ecab361ca24709e93/href</a></iframe><p>Output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oDvZiCxj-bjEwEYleZlxBA.png" /></figure><p>A caveat I discovered with using Python for shell scripting, is that child processes are not terminated when the parent Python process dies. A solution I found for that, which is embedded in the utility, is using the exit hook [ at_exit() ] to kill any child processes that are not terminated yet. This approach will work for soft kills like Ctrl-C, but not for a more aggressive kill like when your python script is terminated using kill -9 , and may leave the child shell command running. I am open to new ideas on how to work-around this drawback. However since most shell scripts are executing short-lived commands, I don&#39;t see this as a showstopper.</p><p>How to consume:</p><p>Just copy the <a href="https://github.com/davidohana/peasyshell/blob/main/peasyshell.py">peasyshell.py</a> file near your Python shell script, and import it.</p><ul><li><a href="https://github.com/davidohana/peasyshell/blob/main/sample_app.py">Another usage sample</a></li><li>GitHub <a href="https://github.com/davidohana/peasyshell">repo</a></li></ul><p>The code is distributed under the Apache v2 OSS license.</p><p><strong>Tip</strong>: You can make your Python script executable:</p><ol><li>Add a shebang line at the top of your script:</li></ol><pre>#!/usr/bin/env python2</pre><pre>from peasyshell import *<br>...</pre><p>2. Make your script executable:</p><pre>chmod +x my_app.py</pre><p>3. Run:</p><pre>./my_app.py</pre><p>Have fun scripting.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=44bba8ba1e9e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/avoiding-bash-frustration-use-python-for-shell-scripts-44bba8ba1e9e">Avoiding Bash frustration — Use Python for shell scripts</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Simple Runtime Profiling of Batch Jobs in Production]]></title>
            <link>https://medium.com/swlh/simple-runtime-profiling-of-batch-jobs-in-production-ddd59e192924?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/ddd59e192924</guid>
            <category><![CDATA[batch-processing]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[profiling]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[kotlin]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Mon, 02 Nov 2020 14:21:49 GMT</pubDate>
            <atom:updated>2020-11-05T11:42:48.611Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EoTN-O9P4jZNGDI_k_CTLQ.jpeg" /></figure><p>So, you have a batch processing / ETL task that receives some data in a loop or per request and crunches it. It might even be running in production. All is working well but now you need to optimize it a bit in order to increase the rate of data you can handle.</p><p>In this post, I will introduce a simple yet effective approach to do so, which you can even run in production to measure the performance of real-world workloads. I will supply a short (single file, no dependencies) implementation for the profiler for both <strong>Kotlin</strong> and <strong>Python.</strong></p><p>So why <em>not</em> use widely-available profiling tools like VisualVM / JProfiler / cProfile?</p><ul><li>It is not easy to instrument those tools for running in a production environment. You may need to add extra dependencies, different bootstrapping code, etc..</li><li>Some of the profilers have a significant overhead on runtime since they measure every method and code line (even in sampling mode). You don&#39;t want to deploy such a profiled application to production as it will affect your processing rate significantly.</li><li>The profiling output is way too verbose, again, since every function or code-line is measured.</li><li>Most of the profilers provide a summary output at the end of the run. What if you want to log/print profiling results periodically while still running?</li><li>How do you deal with warm-up times and changes in workloads while the application Is running?</li><li>Some profilers use stack-trace sampling as a data-source, therefore they measure run times for whole functions only. This may force you to refactor code blocks to sub-functions.</li><li>Some of the available profiling tools are commercial.</li></ul><h3>SimpleProfiler</h3><p>The approach I suggest is very simple. Your task probably runs in a loop or on a per-call basis. Call the profiler’s start_section(section_name) before any code block you want to measure, and end_section() after the code block. The profiler will collect running-time statistics for this code block.</p><p>You may call report() to get a profiling summary like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2e2460cda27aaf6ab463f6428d070b15/href">https://medium.com/media/2e2460cda27aaf6ab463f6428d070b15/href</a></iframe><p>We can see a line per section, measuring:<br>(1) The cumulative runtime of all code inside that section. <br>(2) The contribution (in %) of this section to the total runtime.<br>(3) Count of time this section was executed.<br>(4) Average runtime of 1000 executions of this section.<br>(5) Frequency/Rate — Count of times this section can be executed in 1 second.</p><p>Note that sections can overlap or be nested. In the example output above, parser section includes both split , <a href="https://github.com/IBM/Drain3">drain</a> and mask section.</p><p>The profiler also supports an optional <em>enclosing section</em>. This section (total above) typically covers a full single iteration/request and all other sections are sub-sections of it. It serves as a reference for what is 100% of run-time and by summing runtime of all nested sections, you may determine whether there are some other code-blocks which take significant run-time but are not enclosed in any section.</p><h4><strong>Dealing with warm-up time and changing workloads</strong></h4><p>Sometimes you want to exclude from profiling the first iterations of batch processing until everything is cached properly. There are also cases, in a long-running application, where workloads/request content changes over time, for example, if the work to do is dependent on a data-structure that is growing gradually. <br>In order to deal with such cases, the profiler supports an optional resetAfterSampleCount argument (0 by default), which will also calculate rates statistics for up to <em>n</em> last executions of each section. Those rates (runtime per 1000 executions and frequency), will be displayed in the report in parenthesis next to the cumulative global rates.</p><p><strong>Notes</strong></p><p>The printer argument of the SimpleProfiler class allows you to supply a function which may write report output to a destination other than the default <em>stdout</em> (for example, log)</p><p>SimpleProfiler also implements a general Profiler interface, so that you can replace it with NullProfiler, in order to disable profiling without removing profiling sections from your code, and avoiding if statements and null-checks.</p><p>Overhead — the profiler overhead is mainly dependant on the duration of a single workload cycle/iteration/request. The Kotlin profiler took about 0.8% overhead when a single cycle run took 0.1 ms, but 28.2% overhead when a single cycle run took 0.00036ms. In Python, overheads were 3.4% and 31.3% respectively. Conclusion — this profiling approach is less suitable for profiling micro workloads.</p><p>The profiler is not (yet) thread-safe. This means it is suitable for profiling tasks that run in a single thread only. I intend to add multithreading support soon, should not be a big deal...</p><h3>Demo Use-Case: comparing hashing algorithms performance: Python vs. Java.</h3><p>The following code snippets profiles calculation of md5, sha1 and sha256hashes for 10000-bytes array using the standard librariesjava.security.MessageDigest and Python’s hashlib. I also added calculation of square root for a random number.</p><p>In <strong>Kotlin</strong>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8ac32d3617ed7ba355ad16eef5c4ec45/href">https://medium.com/media/8ac32d3617ed7ba355ad16eef5c4ec45/href</a></iframe><p>In <strong>Python</strong>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/817b73da212fb665e37fdb6eeb70fa30/href">https://medium.com/media/817b73da212fb665e37fdb6eeb70fa30/href</a></iframe><p>Output (Kotlin):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/022bf1213db250a919292437bd8abf80/href">https://medium.com/media/022bf1213db250a919292437bd8abf80/href</a></iframe><p>Output (Python):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/342db05c114d42f551b42167153325a6/href">https://medium.com/media/342db05c114d42f551b42167153325a6/href</a></iframe><p>And few insights from this little experiment:</p><ul><li>Hashing in Python is about 2-times faster than in Java (well in Python it is actually <a href="https://github.com/python/cpython/blob/master/Modules/sha256module.c">C implementation</a>)</li><li>sha256 is the slowest in Python and sha1 is the slowest in Java.</li><li>Initialization of hashing algorithms in Java is very slow.</li><li>sqrt and random are much slower in Python.</li><li>Generally, pure Python code runs slower than JVM code (we can learn that from the 0.09ms profiling overhead in Kotlin and 0.32ms profiling overhead in Python).</li></ul><h3><strong>Profiler Implementation</strong></h3><p>Single file with less than 150 lines, no 3rd party dependencies.<br>It&#39;s an Apache v2 license, just copy the file to your project to use it.</p><ul><li><a href="https://github.com/davidohana/SimpleProfiler/blob/main/src/main/kotlin/davidoh/profiling/SimpleProfiler.kt">In Kotlin</a></li><li><a href="https://github.com/davidohana/SimpleProfiler/blob/main/python/simple_profiler.py">In Python</a> (3.6 or later)</li></ul><p>GitHub <a href="https://github.com/davidohana/SimpleProfiler">repository</a> with full code and samples. Star it if you like it :-)</p><p>— Happy profiling!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ddd59e192924" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/simple-runtime-profiling-of-batch-jobs-in-production-ddd59e192924">Simple Runtime Profiling of Batch Jobs in Production</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Code-First Configuration Library for Kotlin]]></title>
            <link>https://medium.com/swlh/code-first-configuration-library-for-kotlin-75721310f37c?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/75721310f37c</guid>
            <category><![CDATA[ini]]></category>
            <category><![CDATA[libraries]]></category>
            <category><![CDATA[json]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[configuration]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Thu, 15 Oct 2020 13:18:20 GMT</pubDate>
            <atom:updated>2020-10-16T08:45:00.818Z</atom:updated>
            <content:encoded><![CDATA[<p>GitHub repo: <a href="https://github.com/davidohana/kofiko-kotlin">https://github.com/davidohana/kofiko-kotlin</a></p><h3>Preface</h3><p>Kotlin and Python are my favorite programming languages. After publishing <a href="https://medium.com/swlh/code-first-configuration-approach-for-python-f975469433b9">Kofiko configuration library for Python</a>, I decided to work on a port of it for Kotlin. Actually the porting to Kotlin took significantly more effort, for many reasons. I wanted to introduce better extensibility architecture this time, I wanted the library to support more formats, and also due to many conceptual differences between Kotlin and Python. For example, Kotlin Annotations can contain metadata only, and Python Decorators can contain logic.</p><p>Other challenges involved were how to discover configuration objects, how to design a fluent API for adding configuration layers (providers), and a lot of reflection work.</p><p>Though I tried to keep the library clean from any external dependency, I finally settled on a single dependency in the well-known <em>jackson.core</em> (ObjectMapper) library, in order to be able to reuse string parsing ability of Jackson without having to write many type converters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*w3NpMt76wfPl9qFEQscxKg.png" /></figure><p>Kofiko (Kode-First Konfiguration) is a lightweight, simple and minimal boilerplate configuration library for Kotlin.</p><ul><li>Supported formats: .json, .ini, .properties, .env (more to come)</li><li>Layered (cascading) and extensible design allows overriding the configuration from environment variables, command-line arguments, JVM system properties (-D), Java Maps in any precedence order you like.</li></ul><h3>Demo</h3><p><strong>Define application configuration as Kotlin classes/objects:</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e77ac8909b94de242ab580faf0aa8ab8/href">https://medium.com/media/e77ac8909b94de242ab580faf0aa8ab8/href</a></iframe><p>Each config section is represented by a class / object so that configuration consumers may receive only the configuration of interest. Configuration options should be declared as var properties (read/write) with baked-in defaults. By using Kotlin object, you may easily access configuration as a singleton without injection. However, instances of configuration classes may be configured as well.</p><p><strong>Override default values at run-time:</strong></p><p>For example, from a JSON file:</p><pre>{<br>  &quot;database&quot;: {<br>    &quot;user&quot;: &quot;davidoh&quot;,<br>    &quot;db_size_limits&quot;: {<br>      &quot;logs&quot;: 1,<br>      &quot;events&quot;: 120<br>    }<br>  }<br>}</pre><p>or using <em>env. vars</em>:</p><pre>DATABASE_user=davidoh \<br>DATABASE_password=reallysecret! \<br>DATABASE_endpoints=prod1,prod2 \<br>LOG_level=WARNING \<br>DATABASE_DB_SIZE_LIMITS=logs:5,events:120 \<br>java -cp my_app.jar</pre><p>Kofiko uses out-of-the-box (configurable) conventions to search for matching configuration entries, looking for lowercase, uppercase, camel-case, snake-case, kebab-case matches.</p><p><strong>Initialize Kofiko with the desired configuration sources:</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/793633347a58eb0f8bfacffc589344f1/href">https://medium.com/media/793633347a58eb0f8bfacffc589344f1/href</a></iframe><p>Program output:</p><pre>LogConfig.level was changed from &lt;INFO&gt; to &lt;WARNING&gt; by IniConfigProvider<br>WARNING: Hello Kofiko<br>DatabaseConfig.user was changed from &lt;default_user&gt; to &lt;davidoh&gt; by IniConfigProvider<br>DatabaseConfig.password was changed from &lt;[hidden]&gt; to &lt;[hidden]&gt; by IniConfigProvider<br>DatabaseConfig.endpoints was changed from &lt;[http://localhost:1234]&gt; to &lt;[prod1, prod2]&gt; by IniConfigProvider<br>DatabaseConfig.dbSizeLimits was changed from &lt;{alerts=50, logs=200}&gt; to &lt;{alerts=2, logs=1}&gt; by JsonConfigProvider<br>Database user is davidoh</pre><p>Kofiko can print/log the effective configuration overrides, omitting secret info like passwords.</p><p>The source code for Kofiko is available on <a href="https://github.com/davidohana/kofiko">GitHub</a> under the Apache-2.0 license. <br>For further details, please refer to the GitHub repository of the project at <a href="https://github.com/davidohana/kofiko-kotlin">https://github.com/davidohana/kofiko-kotlin</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=75721310f37c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/code-first-configuration-library-for-kotlin-75721310f37c">Code-First Configuration Library for Kotlin</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Code-First Configuration approach for Python]]></title>
            <link>https://medium.com/swlh/code-first-configuration-approach-for-python-f975469433b9?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/f975469433b9</guid>
            <category><![CDATA[ini]]></category>
            <category><![CDATA[code-first]]></category>
            <category><![CDATA[environment-variables]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[config]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Sun, 28 Jun 2020 14:52:33 GMT</pubDate>
            <atom:updated>2020-10-15T12:13:22.434Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/576/1*qMT09bz5T7AgCzgCYhzSmg.png" /></figure><p>GitHub repo: <a href="https://github.com/davidohana/kofiko-python">https://github.com/davidohana/kofiko-python</a><br>PyPi package: <a href="https://pypi.org/project/kofiko/">https://pypi.org/project/kofiko</a></p><p>In the Code-First approach, you first define your data-model in plain code. You can start working with that model immediately, and only later you worry about schema definitions, bindings, and other necessities. The mapping between the domain model and external entities like database tables/fields usually relies on <a href="https://en.wikipedia.org/wiki/Convention_over_configuration">conventions</a>.</p><p>What I like about this approach is that it let you focus on the most important things first, and not less important, you have all the convenience of a modern IDE when defining your model — refactorings, code completion, type checks, etc..</p><p>After my l<a href="https://medium.com/@davidoha/layered-python-configparser-wrapper-with-support-for-environment-vars-965555a58f0b">ast attempt in creating a configuration library</a> for Python, I was still not satisfied and looked for a way to implement the code-first approach for configuration. The outcome is a Python package named <a href="https://github.com/davidohana/kofiko">kofiko</a> — “<strong>Ko</strong>de <strong>Fi</strong>rst <strong>Ko</strong>nfiguration” (which is also a funny ape from an old Israeli children book series). I am pretty satisfied with the outcome, which is described next.</p><p>First, you define the desired configuration settings in plain Python code. Configuration entries are defined as static attributes of a class, with a default value which also defines the type of each entry. You may define many configuration classes — each class serves as a different config section. It can be located anywhere in your code.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/26728c13f0b03e76fffe097bd332a847/href">https://medium.com/media/26728c13f0b03e76fffe097bd332a847/href</a></iframe><p>In the example above, for example, the log bootstrapping code that uses the config class above is not interested in any configuration options other than log-related, so its reasonable to define a dedicated config class for log and locate it side-by-side with the bootstrapping code.</p><p>The @config_section decorator above is actually registering this class with Kofiko. Once kofiko.configure() function is called, it will look-up for configuration overrides in various sources and set the value of relevant attributes of the class accordingly.</p><p>Currently, Kofiko supports the following configuration sources: <br>(1) Customization functions<br>(2) .INIfiles<br>(3) Env. vars</p><p>Customization functions allow you to stay in the code-first approach, by defining simple Python functions that override selective variables in the default configuration classes. This is useful for example in cases where you have multiple deployments. You can create a customization class for each deployment.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2c537efeeac38e2ab4200bd04a481540/href">https://medium.com/media/2c537efeeac38e2ab4200bd04a481540/href</a></iframe><p>You have to register the customization function with Kofiko using the @config_custom decorator, the same way we did with the configuration class.</p><p>Kofiko also supports the familiar .INI format. You can specify one ore more in filenames to search for overrides. Kofiko maps the config class name to an ini section and the attribute name to an ini option:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7ec00ae8dd87f70ca9b79b4c757b7a56/href">https://medium.com/media/7ec00ae8dd87f70ca9b79b4c757b7a56/href</a></iframe><p>Note that you can even omit the Config keyword from the section name.</p><p>The last supported override source is environment variables. Kofiko will look-up for env keys that matches the following convention app-prefix_section_option, and override config attributes accordingly. For example, we can run our app like this:</p><pre>log_file_out_folder=../log-staging elastic_env_name=staging mirror_batch_hours=1 python my_app.py</pre><p>In this case, we didn’t use any app-specific lookup prefix, but we can always opt for use such, in order to prevent collisions with other env vars.</p><p>Also, note that the lookups in ini and env-vars are case-insensitive by default. And if you don&#39;t like my default conventions for override lookups, Kofiko also allows you to customize those with your own.</p><p>One of the nicest things about Kofiko is that you don&#39;t have to do type-conversion any more. Kofiko will use the default value for each config attribute as a type-hint and will try to convert the text value read from untyped override sources (ini and env) to the same type. It will fallback to string only when unable to convert. <br>In addition to the basics (str, int, float &amp; bool ), I added support for parsing list (comma delimited by default) and dict in the format key1:val1,key2:val2. The type conversion for list values and dict keys and values is done using the first element in the default value for the relevant config attribute (if exist).</p><pre>database_endpoints=host1,host2 database_quotas=logs:300,alerts:50 python my_db_client.py</pre><p><strong>Bootstrapping</strong></p><p>In the initialization code of your Python app, all you have to do is call the static kofiko.configure() function. You may specify a customization and/or ini files for lookup like this:</p><pre>overrides = kofiko.configure(<br>   customization_name=&quot;prod&quot;, <br>   ini_file_names=&quot;../cfg/prod.ini&quot;)</pre><p>After this call, attributes values in all config classes are overridden from relevant override sources, and you can use those config classes directly in your code.</p><p>The overrides return values holds a dictionary of all values that were changed from their defaults. It might be useful to log or print this.</p><p><strong>How to get it</strong></p><p>The source code for Kofiko is available on <a href="https://github.com/davidohana/kofiko">GitHub</a> under the Apache-2.0 license. You can also install it from <a href="https://pypi.org/project/kofiko/">PyPi</a>:</p><pre>pip install kofiko</pre><p>I hope you will like this little configuration monkey, and please comment and tell me what you thought about it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f975469433b9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/code-first-configuration-approach-for-python-f975469433b9">Code-First Configuration approach for Python</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to restrict user access with to Grafana with Generic OAuth]]></title>
            <link>https://davidoha.medium.com/how-to-restrict-user-access-with-to-grafana-with-generic-oauth-656a1a660a7b?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/656a1a660a7b</guid>
            <category><![CDATA[grafana]]></category>
            <category><![CDATA[authentication]]></category>
            <category><![CDATA[oauth]]></category>
            <category><![CDATA[api]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Wed, 17 Jun 2020 12:47:14 GMT</pubDate>
            <atom:updated>2020-06-17T13:32:31.931Z</atom:updated>
            <content:encoded><![CDATA[<h3>Grafana &amp; Generic OAuth — How to restrict access to specific authenticated users?</h3><p>If you have successfully integrated <a href="https://grafana.com/docs/grafana/latest/auth/generic-oauth/">Generic OAuth</a> with Grafana, you might wonder as I did, <strong>how do you allow only specific authenticated users</strong> from your organization <strong>to access Grafana?</strong> and <strong>how do you set different access rights (admin, editor, viewer) to those users?</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PsqfnJ2Dtr4asTDgpwy0vA.png" /></figure><p>The first thing we need to do is to set disable sign up of new users and anonymous users. This can be done by editing grafana.ini file or setting env vars.</p><pre>[auth.generic_oauth]<br>allow_sign_up = false</pre><pre>[auth.anonymous]<br>enabled = false</pre><p>This will allow only users that are already listed in Grafana’s user database to sign-in.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/217/1*mlcMnUWKMNAf5aK8ZuUSHg.png" /></figure><p>But how do we add users to Grafana?</p><p>Inviting a user using Grafana’s User management dashboard will <strong>not</strong> do the trick, as invited user is not an actual user until the first sign up.</p><p>But, we can add a user programmatically using the <a href="https://grafana.com/docs/grafana/latest/http_api/admin/#global-users">Admin HTTP API</a> with an HTTP POST call:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/da487972ac2ee3eaec192b99bd7a271b/href">https://medium.com/media/da487972ac2ee3eaec192b99bd7a271b/href</a></iframe><p>Now all users added like this will be able to access Grafana. <br>Note that there is a <strong>tricky part</strong> here: Grafana is case sensitive to emails. So the Case of the email address of the user must match exactly to the case returned from the OAuth endpoint.</p><p>Now, how do we set permissions to specific users, such that some are viewers, some are editors and some are admins?</p><p>After you have added a user, her default access level is <em>viewer</em>. You can use the <a href="https://grafana.com/docs/grafana/latest/http_api/admin/#permissions">permissions</a> API call to set a user as global Grafana admin:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f86b7540e2eea295be66a57da104b298/href">https://medium.com/media/f86b7540e2eea295be66a57da104b298/href</a></iframe><p>You will probably also want to set a user as an organization admin/editor, to allow her to edit some dashboards (Global admin cannot do this until explicitly setting herself also as org admin). This can be done using the <a href="https://grafana.com/docs/grafana/latest/http_api/org/#updates-the-given-user">organization API</a>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f5229b8f10ed7ad853b6cc901cb8b118/href">https://medium.com/media/f5229b8f10ed7ad853b6cc901cb8b118/href</a></iframe><p>That&#39;s it. Wrapping all up, here is a sample Python code that reads a user list from a CSV file and adds all of the entries Grafana users. Users with * as first char will be added as admins.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0cbce7904550d45282e5119335598de7/href">https://medium.com/media/0cbce7904550d45282e5119335598de7/href</a></iframe><figure><img alt="" src="https://cdn-images-1.medium.com/max/974/1*SzsaKipGKVV2V-hZin5Klw.png" /><figcaption>User list after running add_grafana_users.py</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=656a1a660a7b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Python Retry on Exception]]></title>
            <link>https://davidoha.medium.com/python-retry-on-exception-d36fa58df4e1?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/d36fa58df4e1</guid>
            <category><![CDATA[functional-programming]]></category>
            <category><![CDATA[retry]]></category>
            <category><![CDATA[exception]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Mon, 25 May 2020 15:35:38 GMT</pubDate>
            <atom:updated>2020-06-28T11:45:17.805Z</atom:updated>
            <content:encoded><![CDATA[<h3>Never stop trying: Retry on Exception in Python</h3><p>(Full code and samples for this post at my <a href="https://github.com/davidohana/python-retry-func">GitHub Repo</a>)</p><p>Suppose you have the following code which invokes a gRPC request and may fail due to various network conditions.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b64dff89ac49dc3cc14736656bc68a97/href">https://medium.com/media/b64dff89ac49dc3cc14736656bc68a97/href</a></iframe><p>How to retry this call until no exception raised? Wrap the call in an inner(inline) named function and use the provided <strong>retry</strong> function.<br>Thanks to closures, we can use any variable in the scope outer to the inner function.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/29017d4392c5f89ab6b39f31d0c2b545/href">https://medium.com/media/29017d4392c5f89ab6b39f31d0c2b545/href</a></iframe><p>This retry function supports the following features:</p><ul><li>Returns the value of the invoked function when it succeeds</li><li>Raises the exception of the invoked function if attempts exhausted</li><li>Limit for the number of attempts (0 for unlimited)</li><li>Wait (linear or exponential) between attempts</li><li>Retry only if the exception is an instance of a specific exception type.</li><li>Optional logging of attempts</li></ul><p><strong>Retry function code:</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bab6831d4732ec6403bfb6e95197deb9/href">https://medium.com/media/bab6831d4732ec6403bfb6e95197deb9/href</a></iframe><p>So, keep trying!</p><p>David</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d36fa58df4e1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Python Logging: Colorize Your Arguments!]]></title>
            <link>https://medium.com/analytics-vidhya/python-logging-colorize-your-arguments-41567a754ac?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/41567a754ac</guid>
            <category><![CDATA[colors]]></category>
            <category><![CDATA[ansi]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[formatting]]></category>
            <category><![CDATA[logging]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Sun, 24 May 2020 16:05:03 GMT</pubDate>
            <atom:updated>2020-06-28T11:44:56.779Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZaOru_rZsnfNCQaPJ5D60g.png" /><figcaption>Logging in alternating colors for message arguments</figcaption></figure><p>The latest full code and samples for this article are available under the Apache-2.0 license at my <a href="https://github.com/davidohana/colargulog">GitHub Repo</a>.</p><p>Yes, we love logging in colors.<br>Yes, there are many Python libraries and sample code that show you how to colorize your stdout log messages by logging level.</p><p>But I am going to show you something better — log messages with <strong>alternating colors</strong> for each argument in the format string, in addition to <strong>colorization by log level</strong>. And a bonus — argument formatting is made using the new “<strong>brace-style</strong>” formatting introduced in Python 3.2, for example:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9d139840234f34558c44f9d38417d94e/href">https://medium.com/media/9d139840234f34558c44f9d38417d94e/href</a></iframe><p>Implementation is simple, and no 3rd party dependencies are introduced. To apply colors, all you need to do is to set the formatter of the StreamHandler to an instance of <strong>ColorizedArgsFormatter:</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/43256d6df8a1f48d86e0257ff0442537/href">https://medium.com/media/43256d6df8a1f48d86e0257ff0442537/href</a></iframe><h4>How does it work?</h4><ul><li>Each log record is inspected to determine if brace-style formatting is used. I decided to use brace style formatting because it makes it easier to identify the start and end of a formatting parameter: {param}</li><li>ANSI escape code for the current alternating color is added before each formatting parameter, and reset color escape code is added after it</li><li>LogRecord is updated so that <strong>message</strong> field is formatted using str.format() and <strong>args</strong> field is set to empty.</li><li>ANSI escape code for the specific level is added before <strong>levelname</strong> and <strong>levelno</strong> formatting placeholders, and reset color escape code is added after the placeholder.</li></ul><h4>Compatibility Challenges</h4><ul><li><strong>We still need to support legacy-style string formatting</strong>, e.g:<br><em>logger.info(“My name is %s and my age is %d”, “Dave”, 12)<br></em>as many 3rd party dependencies of our code might log in the old format.<br>My solution for this is using a few simple heuristics to identify whether brace-style formatting should be used: no ‘%’ character in string + number of curly braces pair matches the number of log record arguments. Otherwise, we fall-back to legacy formatting, but no argument colorization is available.</li><li><strong>We have to support brace-style formatting also when logging to other mediums, like files</strong>. Otherwise, the logger will fail to understand format string with “TypeError: not all arguments converted during string formatting”. Solution for that is using another simple formatter named <strong>BraceFormatStyleFormatter</strong> for file logging handlers and other mediums that do not support colors. This formatter is very similar to <strong>ColorizedArgsFormatter</strong>, however, it only performs rewriting of log record with the formatted message, and not adding ANSI colors escape codes.</li></ul><h4>Other Tips</h4><ul><li>By default, there are two alternating colors. You can change the colors or add more alternating colors by changing the <em>arg_colors</em> list.</li><li>It is possible to alter the mapping of log levels to colors by changing the <em>level_to_color</em> dictionary.</li><li>Currently, curly brace message formatting with kwargs mapping is not supported in logging, e.g: <em>My name is {name}”.format(name=”David”)</em></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bWzogOG0_V597SKjFnAF7A.png" /><figcaption>Customization — more alternating colors and different color dor DEBUG messages</figcaption></figure><h4>Full code</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d66f6af59bbfb1ddc223775c0008609e/href">https://medium.com/media/d66f6af59bbfb1ddc223775c0008609e/href</a></iframe><h4>Bootstrapping and Logging — Full Sample App:</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/13ca0591227dab5514ef9d10a6f97f91/href">https://medium.com/media/13ca0591227dab5514ef9d10a6f97f91/href</a></iframe><p>The latest full code and samples are available under the Apache-2.0 license at my <a href="https://github.com/davidohana/colargulog">GitHub Repo</a>.</p><p>Have a colorful day,<br>David</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=41567a754ac" width="1" height="1" alt=""><hr><p><a href="https://medium.com/analytics-vidhya/python-logging-colorize-your-arguments-41567a754ac">Python Logging: Colorize Your Arguments!</a> was originally published in <a href="https://medium.com/analytics-vidhya">Analytics Vidhya</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Layered Python ConfigParser wrapper with support for environment vars]]></title>
            <link>https://davidoha.medium.com/layered-python-configparser-wrapper-with-support-for-environment-vars-965555a58f0b?source=rss-418b440883c6------2</link>
            <guid isPermaLink="false">https://medium.com/p/965555a58f0b</guid>
            <category><![CDATA[configuration]]></category>
            <category><![CDATA[package]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[David Ohana]]></dc:creator>
            <pubDate>Wed, 20 May 2020 15:14:59 GMT</pubDate>
            <atom:updated>2020-06-28T11:46:09.292Z</atom:updated>
            <content:encoded><![CDATA[<p>The full code (library + example + .ini files) for the following article are available at GitHub : <a href="https://github.com/davidohana/LayConf">https://github.com/davidohana/LayConf</a></p><p>In every programming language I use, one of the first thing I need is a decent configuration library.</p><p>My requirements are usually:</p><ul><li>Default (hard-coded) configuration</li><li>Custom configuration file</li><li>Ability to override configuration entries from environment variables and command-line arguments</li></ul><p>In Python, the existing packages I found seems to be an overkill, or it might just be my <a href="https://en.wikipedia.org/wiki/Not_invented_here">NIH syndrome</a>. But anyway, after some search, I shamelessly copied and modified <a href="https://medium.com/@unpluggedcoder/python-config-parser-compatible-with-environment-variable-8c5c46145a46">this</a>.</p><p>The result is a small and simple class that supports most of what I need.</p><p>Configuration options are first looked up at env var {section}_{option}, then in a custom .ini file, then in the default .ini file. It&#39;s also possible to add inline defaults if the entry does not exist in the default .ini file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/466/1*mAcBWwbXKBG4ALOwDqeVaA.png" /></figure><p>Example usage:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ed17c88ef9c87bdb8b52f7a0277865ab/href">https://medium.com/media/ed17c88ef9c87bdb8b52f7a0277865ab/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/475e11c7a08dccb55408c4c297931554/href">https://medium.com/media/475e11c7a08dccb55408c4c297931554/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/53e887f82544e3e85acafc2b2cc29147/href">https://medium.com/media/53e887f82544e3e85acafc2b2cc29147/href</a></iframe><p>Now, let&#39;s run it while overriding one config option:</p><pre>% example_LOG_file_backup_count=300 python example_app.py</pre><p>and the output would be:</p><pre>config env prefix: example<br>config default: cfg/default.ini<br>config custom: cfg/staging.ini<br>env_name: staging<br>console_enabled: True<br>file_rotation_size_mb: 10<br>&#39;foo&#39; not found<br>foo: bar<br>foo_number: 33<br>file_enabled: true<br>file_backup_count: 300</pre><p>The full code (library + example + .ini files) are available at GitHub : <a href="https://github.com/davidohana/LayConf">https://github.com/davidohana/LayConf</a></p><p>Happy Coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=965555a58f0b" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>