<?xml version="1.0" encoding="UTF-8"?><feed
  xmlns="http://www.w3.org/2005/Atom"
  xmlns:thr="http://purl.org/syndication/thread/1.0"
  xml:lang="en-US"
  xml:base="https://opensourcehacker.com/wp-atom.php"
   >
	<title type="text">Open Source Hacker</title>
	<subtitle type="text">Pushing the boundaries of free technology</subtitle>

	<updated>2023-02-11T09:12:11Z</updated>

	<link rel="alternate" type="text/html" href="https://opensourcehacker.com" />
	<id>https://opensourcehacker.com/feed/atom/</id>
	<link rel="self" type="application/atom+xml" href="http://opensourcehacker.com/feed/atom/" />

	
	<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[The State of Python in Blockchain 2023 report]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2023/02/10/the-state-of-python-in-blockchain-2023-report/" />
		<id>https://opensourcehacker.com/?p=3124</id>
		<updated>2023-02-11T09:12:11Z</updated>
		<published>2023-02-10T10:07:06Z</published>
		<category scheme="https://opensourcehacker.com" term="python" /><category scheme="https://opensourcehacker.com" term="blockchain" />		<summary type="html"><![CDATA[The State of Python in Blockchain 2023 report is out now.  <a href="https://opensourcehacker.com/2023/02/10/the-state-of-python-in-blockchain-2023-report/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2023/02/10/the-state-of-python-in-blockchain-2023-report/"><![CDATA[<div data-reddit-rtjson="{&quot;entityMap&quot;:{&quot;0&quot;:{&quot;type&quot;:&quot;LINK&quot;,&quot;mutability&quot;:&quot;MUTABLE&quot;,&quot;data&quot;:{&quot;url&quot;:&quot;https://tradingstrategy.ai/blog/the-state-of-python-in-blockchain-in-2023&quot;}}},&quot;blocks&quot;:[{&quot;key&quot;:&quot;3btbh&quot;,&quot;text&quot;:&quot;Hi all,&quot;,&quot;type&quot;:&quot;unstyled&quot;,&quot;inlineStyleRanges&quot;:[],&quot;entityRanges&quot;:[],&quot;data&quot;:{}},{&quot;key&quot;:&quot;c2dqk&quot;,&quot;text&quot;:&quot;I have been co-authoring the report \&quot;The state of Python in the blockchain 2023.\&quot; The report overviews the modern blockchain development ecosystem for Python developers. Despite turbulences in the markets, we see the year-over-year growth of developer numbers. The demand for Python developers in the industry is growing: Last year, we organised PyChain 2022 conference where we got 1200 signups.&quot;,&quot;type&quot;:&quot;unstyled&quot;,&quot;inlineStyleRanges&quot;:[],&quot;entityRanges&quot;:[],&quot;data&quot;:{}},{&quot;key&quot;:&quot;3irap&quot;,&quot;text&quot;:&quot;Python-based open-source solutions lead in two categories: ETL (extract, transform, load) and security and auditing (static linting, formal verification). Python is also heavily used in development and devops toolchains (smart contract compilation) and integration (RPC clients).&quot;,&quot;type&quot;:&quot;unstyled&quot;,&quot;inlineStyleRanges&quot;:[],&quot;entityRanges&quot;:[],&quot;data&quot;:{}},{&quot;key&quot;:&quot;8tpn4&quot;,&quot;text&quot;:&quot;If you are a Python developer and looking for opportunities in the blockchain ecosystem, take a look, and you may find a lot of interesting projects that are hiring. The report is also good to read if you just want to understand what's happening in the blockchain industry without all the noise of market news.&quot;,&quot;type&quot;:&quot;unstyled&quot;,&quot;inlineStyleRanges&quot;:[],&quot;entityRanges&quot;:[],&quot;data&quot;:{}},{&quot;key&quot;:&quot;eje67&quot;,&quot;text&quot;:&quot;You can read the report here.&quot;,&quot;type&quot;:&quot;unstyled&quot;,&quot;inlineStyleRanges&quot;:[],&quot;entityRanges&quot;:[{&quot;offset&quot;:0,&quot;length&quot;:28,&quot;key&quot;:0}],&quot;data&quot;:{}}]}">
<div class="" data-block="true" data-editor="1f1c68" data-offset-key="6cf0b-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="1e6ik-0-0"><span data-offset-key="1e6ik-0-0">I have been co-authoring the report <a href="https://tradingstrategy.ai/blog/the-state-of-python-in-blockchain-in-2023">The state of Python in the blockchain 2023</a>. The report overviews the modern blockchain development ecosystem for Python developers. Despite turbulences in the markets, we see the year-over-year growth of developer numbers. The demand for Python developers in the industry is growing: Last year, we organised PyChain 2022 conference, where we got 1200 signups.</span></div>
<div data-offset-key="1e6ik-0-0"></div>
</div>
<div class="" data-block="true" data-editor="1f1c68" data-offset-key="95bqa-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="95bqa-0-0"><span data-offset-key="95bqa-0-0">Python-based open-source solutions lead in two categories: ETL (extract, transform, load) and security and auditing (static linting, formal verification). Python is also heavily used in development and devops toolchains (smart contract compilation) and integration (RPC clients).</span></div>
<div data-offset-key="95bqa-0-0"></div>
</div>
<div class="" data-block="true" data-editor="1f1c68" data-offset-key="1gbo0-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="1gbo0-0-0"><span data-offset-key="1gbo0-0-0">If you are a Python developer and looking for opportunities in the blockchain ecosystem, take a look, and you may find a lot of interesting projects that are hiring. The report is also good to read if you just want to understand what&#8217;s happening in the blockchain industry without all the noise of market news.</span></div>
<div data-offset-key="1gbo0-0-0"></div>
</div>
<div class="" data-block="true" data-editor="1f1c68" data-offset-key="4n0ql-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="4n0ql-0-0"><a class="_1FRfMxEAy__7c8vezYv9qP" href="https://tradingstrategy.ai/blog/the-state-of-python-in-blockchain-in-2023"><span data-offset-key="4n0ql-0-0">You can read the report here</span></a><span data-offset-key="4n0ql-1-0">.</span></div>
</div>
</div>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2023/02/10/the-state-of-python-in-blockchain-2023-report/#comments" thr:count="0"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2023/02/10/the-state-of-python-in-blockchain-2023-report/feed/atom/" thr:count="0"/>
		<thr:total>0</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[Building cryptocurrency site with Svelte, Python and TimescaleDB]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2022/01/07/building-cryptocurrency-site-with-svelte-python-and-timescaledb/" />
		<id>https://opensourcehacker.com/?p=3116</id>
		<updated>2022-01-07T12:02:50Z</updated>
		<published>2022-01-07T12:02:50Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" />		<summary type="html"><![CDATA[A technical overview of Trading Strategy protocol (tradingstrategy.ai) software stack. <a href="https://opensourcehacker.com/2022/01/07/building-cryptocurrency-site-with-svelte-python-and-timescaledb/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2022/01/07/building-cryptocurrency-site-with-svelte-python-and-timescaledb/"><![CDATA[<p><em>This blog post is a repost of<a href="https://tradingstrategy.ai/blog/building-cryptocurrency-website"> the original technical overview of the Trading Strategy protocol software stack for community feed aggregators</a>. Please read the original blog post for the best layout and formatting.</em></p>
<p>The audience of this post is software developers who are looking to build scalable software-as-a-service solutions and are interested in Svelte, Python and TimescaleDB technologies. Developers who are interested in Web3, Ethereum, cryptocurrency and blockchain technologies will also find this post useful.</p>
<p><!--kg-card-begin: html--></p>
<p><!--kg-card-end: html--><a name="what-are-trading-strategy-protocol-and-algorithmic-trading"></a></p>
<h2 id="what-are-trading-strategy-protocol-and-algorithmic-trading">What are Trading Strategy protocol and algorithmic trading?</h2>
<p>Trading Strategy is a new service for algorithmic and technical trading of cryptocurrencies on <a href="https://tradingstrategy.ai/docs/glossary.html#term-Decentralised-exchange" rel="external">decentralised exchange (DEXes)</a>.</p>
<p>Algorithmic trading is a derivative of <a href="https://tradingstrategy.ai/docs/glossary.html#term-Technical-analysis" rel="external">technical analysis</a>; taking trading positions based on pure mathematics and data. Algorithmic trading is part of quantitative finance, the opposite of value investing where trading decisions are made based on fundamentals. Algorithmic trading provides a systematic approach to trading compared to methods based on trader intuition or instinct. Whereas technical analysis often aids humans to take trading positions, in its purest form in algorithmic trading a trading program follows a set of trading rules and independently executes trades on the market 24/7.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-4.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-4.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-4.png 1000w, https://trading-strategy.ghost.io/content/images/size/w1600/2022/01/image-4.png 1600w, https://trading-strategy.ghost.io/content/images/size/w2400/2022/01/image-4.png 2400w" alt="" width="2000" height="1298" /></figure>
<p>Today, Trading Strategy offers market data feeds for <a href="https://tradingstrategy.ai/docs/glossary.html#term-Uniswap" rel="external">Uniswap compatible</a> exchanges across three different blockchains (<a href="tradingstrategy.ai/trading-view/ethereum" rel="external">Ethereum</a>, <a href="https://tradingstrategy.ai/trading-view/binance" rel="external">Binance Smart Chain</a>, <a href="https://tradingstrategy.ai/trading-view/polygon" rel="external">Polygon</a>). We expect to cover all major decentralised exchanges and exchange types on all major blockchains by the end of the year.</p>
<p>Our mission is to make algorithmic by developing and investing in algorithmic trading strategies easy. Although serving the market data is the first step, Trading Strategy is going to grow beyond the information service website. During the course of 2022, the service will be decentralised, so that the whole software solution, strategy and trade execution will be runnable anyone independently (so-called operating a node).</p>
<p><a href="https://tradingstrategy.ai/blog/announcing-trading-strategy-protocol" rel="external">You can read more about our future plans in our vision blog post</a>.</p>
<p><a name="rich-in-technical-wealth"></a></p>
<h2 id="rich-in-technical-wealth">Rich in technical wealth</h2>
<p>Trading Strategy chose modern architecture and the best components of 2021 for its software development, making its codebase high in technical wealth.</p>
<p>Technical wealth is the opposite of <a href="https://review.firstround.com/forget-technical-debt-heres-how-to-build-technical-wealth" rel="external">technical debt</a>. As Trading Strategy was able to start as a clean slate project, we had the privilege to pick the latest technologies and build our architecture around these. This enables us to build faster and do changes more dynamically. As an example, a lot of out of the box features offered by TimescaleDB would have taken us months if we had built them ourselves.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-8.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-8.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-8.png 1000w, https://trading-strategy.ghost.io/content/images/size/w1600/2022/01/image-8.png 1600w, https://trading-strategy.ghost.io/content/images/2022/01/image-8.png 1796w" alt="" width="1796" height="922" /></figure>
<p>The decentralised finance (DeFi) industry itself is high in technical wealth compared to traditional finance (TradFi). This is due to the use of public ledger (open data), free-as-in-speech codebases (open source) and permissionless public blockchains (open access). This is the opposite of the traditional financial services that are built around closed networks, private APIs and exclusive access. Privileged and siloed software development leads to high maintenance cost long term, as there are no people who are able to work with the code. For references, <a href="https://calpaterson.com/bank-python.html" rel="external">see this blog post on legacy investment bank software</a> and <a href="https://lwn.net/Articles/871195/]" rel="external">Google&#8217;s woes about maintaining their own Linux kernel with 9000+ in-house patches</a>. Writing financial applications for DeFi is 10x  &#8211; 100x more developer productive than writing them for TradFi.</p>
<p><a name="what-is-a-web3"></a></p>
<h2 id="what-is-a-web3">What is a Web3?</h2>
<p>From the software developer point of view, Web3 refers to backend-free services. <em>No logins or registrations are needed. </em>The user data is not processed and stored on a private server, but a public blockchain where the business logic is wired together using smart contracts. Any transaction, or a &#8220;POST request&#8221; in the traditional parlay, is initiated by users themselves, using their private key on their locally stored wallet application.</p>
<figure class="kg-card kg-image-card"><a href="https://login.xyz/" rel="external"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-5.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-5.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-5.png 1000w, https://trading-strategy.ghost.io/content/images/2022/01/image-5.png 1562w" alt="" width="1562" height="946" /></a></figure>
<p>In this kind of model, there are no sysadmin fraud risks, no data loss risks or data liability risks with user identity information. The user is in 100% control. It is a fairer world where users have more control over the revenue streams they generate. Big IT like Facebook and Google have fewer unfair monetization opportunities on user data.</p>
<p>Only some services are a good fit for Web3. At the moment Web3 works best for pseudonymous and public data use cases. This is almost all finance, provenance (NFTs), public social media like Twitter and discussion forums and eCommerce.</p>
<p>Web3 is still early: though most pieces have been figured out in computer science theory, the implementation is still under development. For example, doing database-like traffic (<a href="https://docs.textile.io/threads/" rel="external">ThreadDB</a>) and storage (<a href="https://www.arweave.org/" rel="external">Arweave</a>, <a href="https://www.storj.io/" rel="external">Storj</a>) can be still considered &#8220;alpha&#8221; today.</p>
<p><a name="software-stack-overview"></a></p>
<h2 id="software-stack-overview">Software stack overview</h2>
<p>Below is a walkthrough of how the HTTP requesting of <a href="https://tradingstrategy.ai" rel="external">Trading Strategy website</a> is set up. Because Trading Strategy application is eventually going to be a Web3-like oracle node in a distributed network, everything built <a href="https://swagger.io/resources/articles/adopting-an-api-first-approach/" rel="external">API first</a>. There are no internal APIs &#8211; the website uses the same public market data APIs as everyone else.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/Trading-Strategy-architecture-3-.svg" alt="" width="1431" height="778" /></figure>
<p>Trading Strategy Oracle is a process that indexes blockchain data: DEX trading data, tokens and so on. Oracle and web processes communicate over TimescaleDB. Oracle is responsible for tasks like <a href="https://tradingstrategy.ai/docs/programming/api/candle.html" rel="external">generating OHLCV candle data</a>, generating <a href="https://tradingstrategy.ai/docs/programming/api/liquidity.html" rel="external">liquidity maps</a> and <a href="https://tradingstrategy.ai/docs/programming/referenceprice.html" rel="external">fetching US dollar reference prices</a>. Oracle processes connect to various blockchain GoEthereum nodes that are part of the P2P network for the respective Ethereum Virtual Machine based blockchains.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/Oracle-architecture.svg" alt="" /></figure>
<p><a name="frontend-why-did-we-choose-svelte"></a></p>
<h2 id="frontend-why-did-we-choose-svelte">Frontend: Why did we choose Svelte?</h2>
<p><a href="https://svelte.dev/" rel="external">Svelte is a new JavaScript frontend framework</a> and an alternative to React, Vue.js and Angular. Svelte comes with <a href="https://kit.svelte.dev/" rel="external">an integration package called SvelteKit that adds a standard web server, routing, server-side rendering</a> and other core functionality needed to build a fully functional website.</p>
<p>We considered React for Trading Strategy . React is the de facto frontend framework choice of the cryptocurrency industry. However, even with our extensive experience in React, we chose Svelte because we believe <a href="https://youtu.be/zhl-Cs1-sG4?t=71" rel="external">Svelte is the framework of the future</a>. Svelte offers reactivity with ahead-of-time compiled virtual DOM free approach. Svelte components are very maintanable single files, containing normal HTML template and normal CSS code instead of bastardized &#8220;styles-in-JS&#8221; or domain-specific templating approaches. Writing reactive logic in Svelte is easier than in other frameworks, due to its elegant design making the most developer-friendly frontend framework.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-3.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-3.png 600w, https://trading-strategy.ghost.io/content/images/2022/01/image-3.png 870w" alt="" width="870" height="968" /></figure>
<p>SvelteKit takes the developer experience of Svelte even further, by introducing the concepts of <a href="https://kit.svelte.dev/docs#routing-pages" rel="external">file-system based routing</a>, <a href="https://kit.svelte.dev/docs#loading" rel="external">simple server-side rendering</a> and integrated web server (<a href="https://vitejs.dev/" rel="external">Vite</a>). With SvelteKit&#8217;s batteries included approach an application developer spends less time on plumbing, boilerplate code and debugging async reactivity mess This all translates to better efficiency: the codebase is easier to read and maintain due to standardized coding conventions across open source libraries, components are faster to develop and new developers pick up the pace faster.</p>
<p>We also use components outside Svelte. We use <a href="https://github.com/leeoniya/uPlot" rel="external">uPlot</a> for <a href="https://tradingstrategy.ai/trading-view/ethereum/uniswap-v2/weapon-eth" rel="external">charting</a>. Though the TradingView is the most popular JS framework for technical trading, we are puritans that go with 100% open-source approach as we are building for long term. uPlot was the charting library with an open-source license. For <a href="https://tradingstrategy.ai/trading-view/exchanges" rel="external">our number heavy tables</a> we use <a href="https://datatables.net/" rel="external">DataTable library</a>. For Web3 integration, <a href="https://www.npmjs.com/package/svelte-web3" rel="external">we use web3.js through svelte-web3</a>.</p>
<p><a name="backend-why-did-we-choose-python-pyramid-and-sqlalchemy"></a></p>
<h2 id="backend-why-did-we-choose-python-pyramid-and-sqlalchemy">Backend: Why did we choose Python, Pyramid and SQLAlchemy?</h2>
<p><a href="https://trypyramid.com/" rel="external">Pyramid is a web framework for Python</a> and <a href="https://www.sqlalchemy.org/" rel="external">SQLAlchemy is Python&#8217;s most popular ORM</a>. We have experience writing Python applications since 2006 using Django, Flask and Pyramid. We also have experience writing backends in Node.js <a href="https://nestjs.com/" rel="external">with various frameworks like Next.js</a>.</p>
<p>For developers who know several programming languages, Python is superior choice for the backend. Writing Python programs takes fewer keystrokes. Python is the most readable of all programming languages. Optional typing support is a great way to make the code more static when the team grows.</p>
<p>There is a forever battle with sync vs. async. There is greater maintenance efficiency with <a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/" rel="external">colored function free</a>, linear threaded, Python code. Our workloads are computationally sensitive, not IO sensitive, so using async IO would cause more headaches and we would get no benefits out of it.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-1.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-1.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-1.png 1000w, https://trading-strategy.ghost.io/content/images/2022/01/image-1.png 1510w" alt="" width="1510" height="1294" /></figure>
<p>We chose Pyramid and SQLAlchemy because we are doing <a href="https://swagger.io/resources/articles/adopting-an-api-first-approach/" rel="external">API first</a> backend and complex databasing with our special workload. Trading Strategy currently features tracking of 800k trading pairs (NASDAQ has only 3000). This is not your run of the mill CRUD and admin UI application. For this kind of use case, Pyramid and SQLALchemy are the choice today. This framework combo offers powerful tooling to cover advanced use cases without sacrificing developer productivity or being completely outside of an average backend developer skillset.</p>
<p>For our API tooling and developer communications <a href="https://swagger.io/specification/" rel="external">we chose OpenAPI 3, also known as Swagger</a>. Integration is mostly cost-free: <a href="https://github.com/Pylons/pyramid_openapi3" rel="external">pyramid_openapi3</a> validates <a href="https://tradingstrategy.ai/api/explorer/" rel="external">the API definition</a>, validates requests and replies, and can automatically route payloads to their corresponding endpoints and <a href="https://tradingstrategy.ai/api/explorer/" rel="external">offers Swagger interactive API explorer</a>. This all saves us a lot of manual development.</p>
<p><a href="https://dramatiq.io/" rel="external">For background workers, we use Dramatiq library</a> with Redis broker. Dramatiq task server is simpler, much easier to understand and maintain than more well-known Celery.</p>
<p><a name="data-research-why-did-we-choose-jupyter-notebook"></a></p>
<h2 id="data-research-why-did-we-choose-jupyter-notebook">Data research: Why did we choose Jupyter notebook?</h2>
<p>Most quantitative finance in the world uses <a href="https://jupyter.org/" rel="external">Jupyter Notebooks</a> and Python data science libraries like <a href="https://pandas.pydata.org/" rel="external">Pandas</a> and <a href="https://numpy.org/" rel="external">Numpy</a>. This is the strongest suite of Python that no other programming language can match. In <a href="https://pandas.pydata.org/about/" rel="external">fact, Pandas was originally developed by an investment bank</a>.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image.png 1000w, https://trading-strategy.ghost.io/content/images/2022/01/image.png 1486w" alt="" width="1486" height="1544" /></figure>
<p>Trading Strategy algorithm backtesting is done in Jupyter. <a href="https://pypi.org/project/tradingstrategy/" rel="external">We offer a Trading Strategy client library</a> so that quants can develop their algos against decentralised exchanges without need to know low level blockchain specific details. If you are interested to see <a href="https://tradingstrategy.ai/docs/programming/examples/getting-started.html" rel="external">if you can beat the market please see Getting started tutorial</a> (beta warning: <a href="https://tradingstrategy.ai/community" rel="external">better to join our Discord as well.</a>)</p>
<p><a name="database-why-did-we-choose-timescaledb"></a></p>
<h2 id="database-why-did-we-choose-timescaledb">Database: Why did we choose TimescaleDB?</h2>
<p><a href="https://www.timescale.com/" rel="external">TimescaleDB</a> is a <a href="https://www.postgresql.org/" rel="external">PostgreSQL extension</a> specialised in <a href="https://blog.timescale.com/blog/what-the-heck-is-time-series-data-and-why-do-i-need-a-time-series-database-dcf3b1b18563/" rel="external">time-series data</a>, especially the &#8220;big&#8221; flavour of it. The alternatives included Clickhouse and xxx.</p>
<p>We chose TimescaleDB, because they are based on PostgreSQL. During the last 20 years, PostgreSQL has overtaken as the most advanced open-source database. PostgreSQL has the most vibrant database ecosystem on this planet. Tuning PostgreSQL is well-known: there are <a href="https://github.com/dalibo/pg_activity" rel="external">multiple products</a> and <a href="https://interjektio.fi/" rel="external">companies</a> to support in-house development.</p>
<p>Many of the core TimescaleDB features make our life easier. For example, <a href="https://docs.timescale.com/timescaledb/latest/overview/how-does-it-compare/timescaledb-vs-postgres/#much-higher-ingest-rates" rel="external">hypertables provide fast insert times on large time-series datasets</a> &#8211; otherwise inserting your one-billionth row starts to slow down. <a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/continuous-aggregates/" rel="external">Continuous aggregates</a> give us free upsampling 1-minute <a href="https://tradingstrategy.ai/docs/glossary.html#term-OHLCV" rel="external">OHLCV candles</a> to 5 minutes to 30 days periods.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-7.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-7.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-7.png 1000w, https://trading-strategy.ghost.io/content/images/size/w1600/2022/01/image-7.png 1600w, https://trading-strategy.ghost.io/content/images/2022/01/image-7.png 1725w" alt="" width="1725" height="1249" /></figure>
<p><a href="https://blog.timescale.com/blog/40-million-to-help-developers-measure-everything-that-matters/" rel="external">TimescaleDB completed their Series B funding round where they raise $40M</a>. Based on the quality of their product, the detail they go in their developer communications, we can see why they could be the winning horse of time-series databases.</p>
<p>TimescaleDB team has been the most responsive of any projects we have seen during the 25 years of open-source involvement. Their proactivity and helpfulness give us an assurance that TimescaleDB is serious about building an open-source community. <a href="https://stackoverflow.com/questions/tagged/timescaledb" rel="external">All of our StackOveflow questions</a> and <a href="https://github.com/timescale/timescaledb/issues/" rel="external">Github issue reports</a>, no matter how bad or novice has gone unanswered. We are pretty sure if one were to ask something offtopic, like making a coffee, and tagging with &#8220;timescaledb&#8221; it would still a receive perfect answer.</p>
<p><a name="why-did-we-launch-on-ethereum-mainnet-binance-smart-chain-and-polygon"></a></p>
<h2 id="why-did-we-launch-on-ethereum-mainnet-binance-smart-chain-and-polygon">Why did we launch on Ethereum mainnet, Binance Smart Chain and Polygon?</h2>
<p>When we started to build Trading Strategy early 2021, the layer 1 blockchains were not still on such rampage as they are today. <a href="tradingstrategy.ai/trading-view/polygon" rel="external">Polygon</a> and <a href="https://tradingstrategy.ai/trading-view/binance" rel="external">Binance Smart Chain</a> were the layer 1 top dogs and no layer two was live yet. Ther chains had user adoption and active DEXes like <a href="https://tradingstrategy.ai/trading-view/binance/pancakeswap-v2" rel="external">PancakeSwap</a> and <a href="https://tradingstrategy.ai/trading-view/polygon/quickswap" rel="external">QuickSwap</a>. Today we have more competition with the likes of Avalanche, Fantom, Aurora and Telos EVM. Also we have non-EVM based solid solutions like NEAR, Elrond and Solana. We expect to integrate all of them during the source of 2022.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-6.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-6.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-6.png 1000w, https://trading-strategy.ghost.io/content/images/size/w1600/2022/01/image-6.png 1600w, https://trading-strategy.ghost.io/content/images/size/w2400/2022/01/image-6.png 2400w" alt="" width="2000" height="1012" /></figure>
<p>Ethereum mainnet has the best developer community. However, the Ethereum mainnet transaction costs are prohibitively expensive for the unforeseeable future, and thus it is unsuitable for our active trading strategies.</p>
<p><a name="what-challenges-do-we-see"></a></p>
<h2 id="what-challenges-do-we-see">What challenges do we see?</h2>
<p>Developing the means failing and retrying a few times, as your first guess is not always right one. We have some good lessons from 2021.</p>
<p><a href="https://svelte.dev/blog/sveltekit-beta" rel="external">SvelteKit is still in beta</a>. Some of the aspects of it are still under development and may not have complete documentation or supporting material like tutorial blog posts. We had to figure out a lot of aspects ourselves, especially what comes to <a href="https://stackoverflow.com/questions/70471512/analyzing-optimizing-and-lazy-loading-vendor-js-when-doing-sveltekit-server-sid/70479348#70479348" rel="external">SvelteKit server-side rendering speed and tuning</a>. We feel the benefits of SvelteKit developer productivity greatly outweighs some learning curve and contributions to the documentation we had to do ourselves.</p>
<p>Svelte is new, thus it still does not have Svelte-native feature-rich charting libraries like uPlot and Datatables. They do not integrate to SvelteKit server-side rendering flow, making it not possible to serve pre-rendered pages.</p>
<p>Hosting Polygon and Binance Smart Chain nodes is tough. Both blockchain teams have issues <a href="https://twitter.com/moo9000/status/1468296100394196992" rel="external">with developer communications</a>. There are no adequate manuals for running your own node. The expectation of using bug-ridden third-party API services goes against the blockchain ethos</p>
<p>Some queries on PostgreSQL are still unnecessary complex whereas other databases do better. Often we need to refer to the latest value, like the latest price, either for a single item or for a group. <a href="e" rel="external">Unfortunately PostgreSQL does not offer any native &#8220;latest value&#8221; indices</a> and it is <a href="https://stackoverflow.com/q/25536422/315168" rel="external">often tricky</a> to write an efficient query for this. <a href="https://dba.stackexchange.com/q/110636/38877" rel="external">Sometimes even simple ORDER LIMIT 1 seems to cause issues for PostgreSQL</a> unless you create unnecessary fat indices.</p>
<p><a name="we-are-open-source"></a></p>
<h2 id="we-are-open-source">We are open source</h2>
<p><a href="https://github.com/tradingstrategy-ai/frontend/" rel="external">Trading Strategy frontend has been open source since the day one</a>. If you are new to Svelte / SvelteKit you might find our repository interesting to read and learn why we have made certain design choices.</p>
<p><a href="https://pypi.org/project/tradingstrategy/" rel="external">The same goes for the Jupyter Python trading strategy client</a>. Oracle code and backend code will be eventually opened, as we start rolling out the protocol network later in 2022.</p>
<p><a name="we-are-hiring"></a></p>
<h2 id="we-are-hiring">We are hiring</h2>
<p>We are currently hiring for frontend (Svelte), backend (Python/PostgreSQL) and quant research (Jupytere Notebook/Pandas) positions. If you are interested to work with cryptocurrencies and algorithmic trading please email us <a rel="external">careers@tradingstrategy.ai</a>.</p>
<p><a href="https://unsplash.com/photos/zGuBURGGmdY" rel="external">The cover photo by Alex Chumak</a>.</p>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2022/01/07/building-cryptocurrency-site-with-svelte-python-and-timescaledb/#comments" thr:count="0"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2022/01/07/building-cryptocurrency-site-with-svelte-python-and-timescaledb/feed/atom/" thr:count="0"/>
		<thr:total>0</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[Building a cryptocurrency site with Svelte, Python and TimescaleDB]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2022/01/07/building-a-cryptocurrency-site-with-svelte-python-and-timescaledb/" />
		<id>https://opensourcehacker.com/?p=3111</id>
		<updated>2022-01-07T11:59:23Z</updated>
		<published>2022-01-07T11:59:23Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" />		<summary type="html"><![CDATA[This blog post is a technical overview of Trading Strategy protocol (tradingstrategy.ai) software stack. <a href="https://opensourcehacker.com/2022/01/07/building-a-cryptocurrency-site-with-svelte-python-and-timescaledb/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2022/01/07/building-a-cryptocurrency-site-with-svelte-python-and-timescaledb/"><![CDATA[<div class="body-text svelte-pf7qve">
<p>This post is <a href="https://tradingstrategy.ai/blog/building-cryptocurrency-website">the repost of the original Trading Strategy software architecture overview</a> for community feed aggregators. Please read the original post for the best layout and formatting.</p>
<p>The audience of this post is software developers who are looking to build scalable software-as-a-service solutions and are interested in Svelte, Python and TimescaleDB technologies. Developers who are interested in Web3, Ethereum, cryptocurrency and blockchain technologies will also find this post useful.</p>
<p><!--kg-card-end: html--><a name="what-are-trading-strategy-protocol-and-algorithmic-trading"></a></p>
<h2 id="what-are-trading-strategy-protocol-and-algorithmic-trading">What are Trading Strategy protocol and algorithmic trading?</h2>
<p>Trading Strategy is a new service for algorithmic and technical trading of cryptocurrencies on <a href="https://tradingstrategy.ai/docs/glossary.html#term-Decentralised-exchange" rel="external">decentralised exchange (DEXes)</a>.</p>
<p>Algorithmic trading is a derivative of <a href="https://tradingstrategy.ai/docs/glossary.html#term-Technical-analysis" rel="external">technical analysis</a>; taking trading positions based on pure mathematics and data. Algorithmic trading is part of quantitative finance, the opposite of value investing where trading decisions are made based on fundamentals. Algorithmic trading provides a systematic approach to trading compared to methods based on trader intuition or instinct. Whereas technical analysis often aids humans to take trading positions, in its purest form in algorithmic trading a trading program follows a set of trading rules and independently executes trades on the market 24/7.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-4.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-4.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-4.png 1000w, https://trading-strategy.ghost.io/content/images/size/w1600/2022/01/image-4.png 1600w, https://trading-strategy.ghost.io/content/images/size/w2400/2022/01/image-4.png 2400w" alt="" width="2000" height="1298" /></figure>
<p>Today, Trading Strategy offers market data feeds for <a href="https://tradingstrategy.ai/docs/glossary.html#term-Uniswap" rel="external">Uniswap compatible</a> exchanges across three different blockchains (<a href="tradingstrategy.ai/trading-view/ethereum" rel="external">Ethereum</a>, <a href="https://tradingstrategy.ai/trading-view/binance" rel="external">Binance Smart Chain</a>, <a href="https://tradingstrategy.ai/trading-view/polygon" rel="external">Polygon</a>). We expect to cover all major decentralised exchanges and exchange types on all major blockchains by the end of the year.</p>
<p>Our mission is to make algorithmic by developing and investing in algorithmic trading strategies easy. Although serving the market data is the first step, Trading Strategy is going to grow beyond the information service website. During the course of 2022, the service will be decentralised, so that the whole software solution, strategy and trade execution will be runnable anyone independently (so-called operating a node).</p>
<p><a href="https://tradingstrategy.ai/blog/announcing-trading-strategy-protocol" rel="external">You can read more about our future plans in our vision blog post</a>.</p>
<p><a name="rich-in-technical-wealth"></a></p>
<h2 id="rich-in-technical-wealth">Rich in technical wealth</h2>
<p>Trading Strategy chose modern architecture and the best components of 2021 for its software development, making its codebase high in technical wealth.</p>
<p>Technical wealth is the opposite of <a href="https://review.firstround.com/forget-technical-debt-heres-how-to-build-technical-wealth" rel="external">technical debt</a>. As Trading Strategy was able to start as a clean slate project, we had the privilege to pick the latest technologies and build our architecture around these. This enables us to build faster and do changes more dynamically. As an example, a lot of out of the box features offered by TimescaleDB would have taken us months if we had built them ourselves.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-8.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-8.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-8.png 1000w, https://trading-strategy.ghost.io/content/images/size/w1600/2022/01/image-8.png 1600w, https://trading-strategy.ghost.io/content/images/2022/01/image-8.png 1796w" alt="" width="1796" height="922" /></figure>
<p>The decentralised finance (DeFi) industry itself is high in technical wealth compared to traditional finance (TradFi). This is due to the use of public ledger (open data), free-as-in-speech codebases (open source) and permissionless public blockchains (open access). This is the opposite of the traditional financial services that are built around closed networks, private APIs and exclusive access. Privileged and siloed software development leads to high maintenance cost long term, as there are no people who are able to work with the code. For references, <a href="https://calpaterson.com/bank-python.html" rel="external">see this blog post on legacy investment bank software</a> and <a href="https://lwn.net/Articles/871195/]" rel="external">Google&#8217;s woes about maintaining their own Linux kernel with 9000+ in-house patches</a>. Writing financial applications for DeFi is 10x  &#8211; 100x more developer productive than writing them for TradFi.</p>
<p><a name="what-is-a-web3"></a></p>
<h2 id="what-is-a-web3">What is a Web3?</h2>
<p>From the software developer point of view, Web3 refers to backend-free services. <em>No logins or registrations are needed. </em>The user data is not processed and stored on a private server, but a public blockchain where the business logic is wired together using smart contracts. Any transaction, or a &#8220;POST request&#8221; in the traditional parlay, is initiated by users themselves, using their private key on their locally stored wallet application.</p>
<figure class="kg-card kg-image-card"><a href="https://login.xyz/" rel="external"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-5.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-5.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-5.png 1000w, https://trading-strategy.ghost.io/content/images/2022/01/image-5.png 1562w" alt="" width="1562" height="946" /></a></figure>
<p>In this kind of model, there are no sysadmin fraud risks, no data loss risks or data liability risks with user identity information. The user is in 100% control. It is a fairer world where users have more control over the revenue streams they generate. Big IT like Facebook and Google have fewer unfair monetization opportunities on user data.</p>
<p>Only some services are a good fit for Web3. At the moment Web3 works best for pseudonymous and public data use cases. This is almost all finance, provenance (NFTs), public social media like Twitter and discussion forums and eCommerce.</p>
<p>Web3 is still early: though most pieces have been figured out in computer science theory, the implementation is still under development. For example, doing database-like traffic (<a href="https://docs.textile.io/threads/" rel="external">ThreadDB</a>) and storage (<a href="https://www.arweave.org/" rel="external">Arweave</a>, <a href="https://www.storj.io/" rel="external">Storj</a>) can be still considered &#8220;alpha&#8221; today.</p>
<p><a name="software-stack-overview"></a></p>
<h2 id="software-stack-overview">Software stack overview</h2>
<p>Below is a walkthrough of how the HTTP requesting of <a href="https://tradingstrategy.ai" rel="external">Trading Strategy website</a> is set up. Because Trading Strategy application is eventually going to be a Web3-like oracle node in a distributed network, everything built <a href="https://swagger.io/resources/articles/adopting-an-api-first-approach/" rel="external">API first</a>. There are no internal APIs &#8211; the website uses the same public market data APIs as everyone else.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/Trading-Strategy-architecture-3-.svg" alt="" width="1431" height="778" /></figure>
<p>Trading Strategy Oracle is a process that indexes blockchain data: DEX trading data, tokens and so on. Oracle and web processes communicate over TimescaleDB. Oracle is responsible for tasks like <a href="https://tradingstrategy.ai/docs/programming/api/candle.html" rel="external">generating OHLCV candle data</a>, generating <a href="https://tradingstrategy.ai/docs/programming/api/liquidity.html" rel="external">liquidity maps</a> and <a href="https://tradingstrategy.ai/docs/programming/referenceprice.html" rel="external">fetching US dollar reference prices</a>. Oracle processes connect to various blockchain GoEthereum nodes that are part of the P2P network for the respective Ethereum Virtual Machine based blockchains.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/Oracle-architecture.svg" alt="" /></figure>
<p><a name="frontend-why-did-we-choose-svelte"></a></p>
<h2 id="frontend-why-did-we-choose-svelte">Frontend: Why did we choose Svelte?</h2>
<p><a href="https://svelte.dev/" rel="external">Svelte is a new JavaScript frontend framework</a> and an alternative to React, Vue.js and Angular. Svelte comes with <a href="https://kit.svelte.dev/" rel="external">an integration package called SvelteKit that adds a standard web server, routing, server-side rendering</a> and other core functionality needed to build a fully functional website.</p>
<p>We considered React for Trading Strategy . React is the de facto frontend framework choice of the cryptocurrency industry. However, even with our extensive experience in React, we chose Svelte because we believe <a href="https://youtu.be/zhl-Cs1-sG4?t=71" rel="external">Svelte is the framework of the future</a>. Svelte offers reactivity with ahead-of-time compiled virtual DOM free approach. Svelte components are very maintanable single files, containing normal HTML template and normal CSS code instead of bastardized &#8220;styles-in-JS&#8221; or domain-specific templating approaches. Writing reactive logic in Svelte is easier than in other frameworks, due to its elegant design making the most developer-friendly frontend framework.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-3.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-3.png 600w, https://trading-strategy.ghost.io/content/images/2022/01/image-3.png 870w" alt="" width="870" height="968" /></figure>
<p>SvelteKit takes the developer experience of Svelte even further, by introducing the concepts of <a href="https://kit.svelte.dev/docs#routing-pages" rel="external">file-system based routing</a>, <a href="https://kit.svelte.dev/docs#loading" rel="external">simple server-side rendering</a> and integrated web server (<a href="https://vitejs.dev/" rel="external">Vite</a>). With SvelteKit&#8217;s batteries included approach an application developer spends less time on plumbing, boilerplate code and debugging async reactivity mess This all translates to better efficiency: the codebase is easier to read and maintain due to standardized coding conventions across open source libraries, components are faster to develop and new developers pick up the pace faster.</p>
<p>We also use components outside Svelte. We use <a href="https://github.com/leeoniya/uPlot" rel="external">uPlot</a> for <a href="https://tradingstrategy.ai/trading-view/ethereum/uniswap-v2/weapon-eth" rel="external">charting</a>. Though the TradingView is the most popular JS framework for technical trading, we are puritans that go with 100% open-source approach as we are building for long term. uPlot was the charting library with an open-source license. For <a href="https://tradingstrategy.ai/trading-view/exchanges" rel="external">our number heavy tables</a> we use <a href="https://datatables.net/" rel="external">DataTable library</a>. For Web3 integration, <a href="https://www.npmjs.com/package/svelte-web3" rel="external">we use web3.js through svelte-web3</a>.</p>
<p><a name="backend-why-did-we-choose-python-pyramid-and-sqlalchemy"></a></p>
<h2 id="backend-why-did-we-choose-python-pyramid-and-sqlalchemy">Backend: Why did we choose Python, Pyramid and SQLAlchemy?</h2>
<p><a href="https://trypyramid.com/" rel="external">Pyramid is a web framework for Python</a> and <a href="https://www.sqlalchemy.org/" rel="external">SQLAlchemy is Python&#8217;s most popular ORM</a>. We have experience writing Python applications since 2006 using Django, Flask and Pyramid. We also have experience writing backends in Node.js <a href="https://nestjs.com/" rel="external">with various frameworks like Next.js</a>.</p>
<p>For developers who know several programming languages, Python is superior choice for the backend. Writing Python programs takes fewer keystrokes. Python is the most readable of all programming languages. Optional typing support is a great way to make the code more static when the team grows.</p>
<p>There is a forever battle with sync vs. async. There is greater maintenance efficiency with <a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/" rel="external">colored function free</a>, linear threaded, Python code. Our workloads are computationally sensitive, not IO sensitive, so using async IO would cause more headaches and we would get no benefits out of it.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-1.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-1.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-1.png 1000w, https://trading-strategy.ghost.io/content/images/2022/01/image-1.png 1510w" alt="" width="1510" height="1294" /></figure>
<p>We chose Pyramid and SQLAlchemy because we are doing <a href="https://swagger.io/resources/articles/adopting-an-api-first-approach/" rel="external">API first</a> backend and complex databasing with our special workload. Trading Strategy currently features tracking of 800k trading pairs (NASDAQ has only 3000). This is not your run of the mill CRUD and admin UI application. For this kind of use case, Pyramid and SQLALchemy are the choice today. This framework combo offers powerful tooling to cover advanced use cases without sacrificing developer productivity or being completely outside of an average backend developer skillset.</p>
<p>For our API tooling and developer communications <a href="https://swagger.io/specification/" rel="external">we chose OpenAPI 3, also known as Swagger</a>. Integration is mostly cost-free: <a href="https://github.com/Pylons/pyramid_openapi3" rel="external">pyramid_openapi3</a> validates <a href="https://tradingstrategy.ai/api/explorer/" rel="external">the API definition</a>, validates requests and replies, and can automatically route payloads to their corresponding endpoints and <a href="https://tradingstrategy.ai/api/explorer/" rel="external">offers Swagger interactive API explorer</a>. This all saves us a lot of manual development.</p>
<p><a href="https://dramatiq.io/" rel="external">For background workers, we use Dramatiq library</a> with Redis broker. Dramatiq task server is simpler, much easier to understand and maintain than more well-known Celery.</p>
<p><a name="data-research-why-did-we-choose-jupyter-notebook"></a></p>
<h2 id="data-research-why-did-we-choose-jupyter-notebook">Data research: Why did we choose Jupyter notebook?</h2>
<p>Most quantitative finance in the world uses <a href="https://jupyter.org/" rel="external">Jupyter Notebooks</a> and Python data science libraries like <a href="https://pandas.pydata.org/" rel="external">Pandas</a> and <a href="https://numpy.org/" rel="external">Numpy</a>. This is the strongest suite of Python that no other programming language can match. In <a href="https://pandas.pydata.org/about/" rel="external">fact, Pandas was originally developed by an investment bank</a>.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image.png 1000w, https://trading-strategy.ghost.io/content/images/2022/01/image.png 1486w" alt="" width="1486" height="1544" /></figure>
<p>Trading Strategy algorithm backtesting is done in Jupyter. <a href="https://pypi.org/project/tradingstrategy/" rel="external">We offer a Trading Strategy client library</a> so that quants can develop their algos against decentralised exchanges without need to know low level blockchain specific details. If you are interested to see <a href="https://tradingstrategy.ai/docs/programming/examples/getting-started.html" rel="external">if you can beat the market please see Getting started tutorial</a> (beta warning: <a href="https://tradingstrategy.ai/community" rel="external">better to join our Discord as well.</a>)</p>
<p><a name="database-why-did-we-choose-timescaledb"></a></p>
<h2 id="database-why-did-we-choose-timescaledb">Database: Why did we choose TimescaleDB?</h2>
<p><a href="https://www.timescale.com/" rel="external">TimescaleDB</a> is a <a href="https://www.postgresql.org/" rel="external">PostgreSQL extension</a> specialised in <a href="https://blog.timescale.com/blog/what-the-heck-is-time-series-data-and-why-do-i-need-a-time-series-database-dcf3b1b18563/" rel="external">time-series data</a>, especially the &#8220;big&#8221; flavour of it. The alternatives included Clickhouse and xxx.</p>
<p>We chose TimescaleDB, because they are based on PostgreSQL. During the last 20 years, PostgreSQL has overtaken as the most advanced open-source database. PostgreSQL has the most vibrant database ecosystem on this planet. Tuning PostgreSQL is well-known: there are <a href="https://github.com/dalibo/pg_activity" rel="external">multiple products</a> and <a href="https://interjektio.fi/" rel="external">companies</a> to support in-house development.</p>
<p>Many of the core TimescaleDB features make our life easier. For example, <a href="https://docs.timescale.com/timescaledb/latest/overview/how-does-it-compare/timescaledb-vs-postgres/#much-higher-ingest-rates" rel="external">hypertables provide fast insert times on large time-series datasets</a> &#8211; otherwise inserting your one-billionth row starts to slow down. <a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/continuous-aggregates/" rel="external">Continuous aggregates</a> give us free upsampling 1-minute <a href="https://tradingstrategy.ai/docs/glossary.html#term-OHLCV" rel="external">OHLCV candles</a> to 5 minutes to 30 days periods.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-7.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-7.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-7.png 1000w, https://trading-strategy.ghost.io/content/images/size/w1600/2022/01/image-7.png 1600w, https://trading-strategy.ghost.io/content/images/2022/01/image-7.png 1725w" alt="" width="1725" height="1249" /></figure>
<p><a href="https://blog.timescale.com/blog/40-million-to-help-developers-measure-everything-that-matters/" rel="external">TimescaleDB completed their Series B funding round where they raise $40M</a>. Based on the quality of their product, the detail they go in their developer communications, we can see why they could be the winning horse of time-series databases.</p>
<p>TimescaleDB team has been the most responsive of any projects we have seen during the 25 years of open-source involvement. Their proactivity and helpfulness give us an assurance that TimescaleDB is serious about building an open-source community. <a href="https://stackoverflow.com/questions/tagged/timescaledb" rel="external">All of our StackOveflow questions</a> and <a href="https://github.com/timescale/timescaledb/issues/" rel="external">Github issue reports</a>, no matter how bad or novice has gone unanswered. We are pretty sure if one were to ask something offtopic, like making a coffee, and tagging with &#8220;timescaledb&#8221; it would still a receive perfect answer.</p>
<p><a name="why-did-we-launch-on-ethereum-mainnet-binance-smart-chain-and-polygon"></a></p>
<h2 id="why-did-we-launch-on-ethereum-mainnet-binance-smart-chain-and-polygon">Why did we launch on Ethereum mainnet, Binance Smart Chain and Polygon?</h2>
<p>When we started to build Trading Strategy early 2021, the layer 1 blockchains were not still on such rampage as they are today. <a href="tradingstrategy.ai/trading-view/polygon" rel="external">Polygon</a> and <a href="https://tradingstrategy.ai/trading-view/binance" rel="external">Binance Smart Chain</a> were the layer 1 top dogs and no layer two was live yet. Ther chains had user adoption and active DEXes like <a href="https://tradingstrategy.ai/trading-view/binance/pancakeswap-v2" rel="external">PancakeSwap</a> and <a href="https://tradingstrategy.ai/trading-view/polygon/quickswap" rel="external">QuickSwap</a>. Today we have more competition with the likes of Avalanche, Fantom, Aurora and Telos EVM. Also we have non-EVM based solid solutions like NEAR, Elrond and Solana. We expect to integrate all of them during the source of 2022.</p>
<figure class="kg-card kg-image-card"><img class="kg-image" src="https://trading-strategy.ghost.io/content/images/2022/01/image-6.png" sizes="(min-width: 720px) 720px" srcset="https://trading-strategy.ghost.io/content/images/size/w600/2022/01/image-6.png 600w, https://trading-strategy.ghost.io/content/images/size/w1000/2022/01/image-6.png 1000w, https://trading-strategy.ghost.io/content/images/size/w1600/2022/01/image-6.png 1600w, https://trading-strategy.ghost.io/content/images/size/w2400/2022/01/image-6.png 2400w" alt="" width="2000" height="1012" /></figure>
<p>Ethereum mainnet has the best developer community. However, the Ethereum mainnet transaction costs are prohibitively expensive for the unforeseeable future, and thus it is unsuitable for our active trading strategies.</p>
<p><a name="what-challenges-do-we-see"></a></p>
<h2 id="what-challenges-do-we-see">What challenges do we see?</h2>
<p>Developing the means failing and retrying a few times, as your first guess is not always right one. We have some good lessons from 2021.</p>
<p><a href="https://svelte.dev/blog/sveltekit-beta" rel="external">SvelteKit is still in beta</a>. Some of the aspects of it are still under development and may not have complete documentation or supporting material like tutorial blog posts. We had to figure out a lot of aspects ourselves, especially what comes to <a href="https://stackoverflow.com/questions/70471512/analyzing-optimizing-and-lazy-loading-vendor-js-when-doing-sveltekit-server-sid/70479348#70479348" rel="external">SvelteKit server-side rendering speed and tuning</a>. We feel the benefits of SvelteKit developer productivity greatly outweighs some learning curve and contributions to the documentation we had to do ourselves.</p>
<p>Svelte is new, thus it still does not have Svelte-native feature-rich charting libraries like uPlot and Datatables. They do not integrate to SvelteKit server-side rendering flow, making it not possible to serve pre-rendered pages.</p>
<p>Hosting Polygon and Binance Smart Chain nodes is tough. Both blockchain teams have issues <a href="https://twitter.com/moo9000/status/1468296100394196992" rel="external">with developer communications</a>. There are no adequate manuals for running your own node. The expectation of using bug-ridden third-party API services goes against the blockchain ethos</p>
<p>Some queries on PostgreSQL are still unnecessary complex whereas other databases do better. Often we need to refer to the latest value, like the latest price, either for a single item or for a group. <a href="e" rel="external">Unfortunately PostgreSQL does not offer any native &#8220;latest value&#8221; indices</a> and it is <a href="https://stackoverflow.com/q/25536422/315168" rel="external">often tricky</a> to write an efficient query for this. <a href="https://dba.stackexchange.com/q/110636/38877" rel="external">Sometimes even simple ORDER LIMIT 1 seems to cause issues for PostgreSQL</a> unless you create unnecessary fat indices.</p>
<p><a name="we-are-open-source"></a></p>
<h2 id="we-are-open-source">We are open source</h2>
<p><a href="https://github.com/tradingstrategy-ai/frontend/" rel="external">Trading Strategy frontend has been open source since the day one</a>. If you are new to Svelte / SvelteKit you might find our repository interesting to read and learn why we have made certain design choices.</p>
<p><a href="https://pypi.org/project/tradingstrategy/" rel="external">The same goes for the Jupyter Python trading strategy client</a>. Oracle code and backend code will be eventually opened, as we start rolling out the protocol network later in 2022.</p>
<p><a name="we-are-hiring"></a></p>
<h2 id="we-are-hiring">We are hiring</h2>
<p>We are currently hiring for frontend (Svelte), backend (Python/PostgreSQL) and quant research (Jupytere Notebook/Pandas) positions. If you are interested to work with cryptocurrencies and algorithmic trading please email us <a rel="external">careers@tradingstrategy.ai</a>.</p>
<p><a href="https://unsplash.com/photos/zGuBURGGmdY" rel="external">The cover photo by Alex Chumak</a>.</p>
</div>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2022/01/07/building-a-cryptocurrency-site-with-svelte-python-and-timescaledb/#comments" thr:count="0"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2022/01/07/building-a-cryptocurrency-site-with-svelte-python-and-timescaledb/feed/atom/" thr:count="0"/>
		<thr:total>0</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[Simple loop parallelization in Python]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2017/01/30/simple-loop-parallelization-in-python/" />
		<id>https://opensourcehacker.com/?p=3102</id>
		<updated>2017-01-30T11:04:57Z</updated>
		<published>2017-01-30T11:04:57Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" /><category scheme="https://opensourcehacker.com" term="concurrent" /><category scheme="https://opensourcehacker.com" term="threadpool" />		<summary type="html"><![CDATA[Sometimes you are programming a loop to run over tasks that could be easily parallelized. Usual suspects include loads that wait IO like calls to third party API services. Since Python 3.2, there have been easy tool for this kind &#8230; <a href="https://opensourcehacker.com/2017/01/30/simple-loop-parallelization-in-python/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2017/01/30/simple-loop-parallelization-in-python/"><![CDATA[<p>Sometimes you are programming a loop to run over tasks that could be easily parallelized. Usual suspects include loads that wait IO like calls to third party API services.</p>
<p>Since Python 3.2, there have been easy tool for this kind of jobs. <a href="https://docs.python.org/3/library/concurrent.futures.html">concurrent.futures</a> standard library module provides thread and multiprocess pools for executing tasks parallel. For older Python versions, a backport library exists.</p>
<p>Consider a loop that waits RPC traffic and the RPC has a wide enough pipe to handle multiple calls simultaneously:</p>
<pre class="prettyprint lang-py">def import_all(contract: Contract, fname: str):
    """Import all entries from a given CSV file."""

    for row in read_csv(fname):
        # This functions performs multiple RPC calls
        # with wait between calls
        import_invoicing_address(contract, row)</pre>
<p>You can create a thread pool that runs tasks on N worker threads. Tasks are wrapped in futures that call the worker function. Each thread keeps consuming tasks from the queue until all of work is done.</p>
<pre class="prettyprint lang-py">import concurrent.futures

def import_all_pooled(contract: Contract, fname: str, workers=32):
    """Parallerized CSV import."""

    # Run the futures within this thread pool
    with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:

        # Stream incoming data and build futures.
        # The execution of futures beings right away and the executor
        # does not wait the loop to be completed.
        futures = [executor.submit(import_invoicing_address, contract, row) for row in read_csv(fname)]

        # This print may be slightly delayed, as futures start executing as soon as the pool begins to fill,
        # eating your CPU time
        print("Executing total", len(futures), "jobs")

        # Wait the executor to complete each future, give 180 seconds for each job
        for idx, future in enumerate(concurrent.futures.as_completed(futures, timeout=180.0)):
            res = future.result()  # This will also raise any exceptions
            print("Processed job", idx, "result", res)

</pre>
<p>If the work is not CPU intensive then Python&#8217;s infamous Global Interpreter Global will not become an issue either.</p>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2017/01/30/simple-loop-parallelization-in-python/#comments" thr:count="1"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2017/01/30/simple-loop-parallelization-in-python/feed/atom/" thr:count="1"/>
		<thr:total>1</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[Deform 2.0]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2016/10/26/deform-2-0/" />
		<id>https://opensourcehacker.com/?p=3097</id>
		<updated>2016-10-25T23:22:39Z</updated>
		<published>2016-10-25T23:22:39Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" /><category scheme="https://opensourcehacker.com" term="bootstrap" /><category scheme="https://opensourcehacker.com" term="deform" /><category scheme="https://opensourcehacker.com" term="html5" /><category scheme="https://opensourcehacker.com" term="pyramid" />		<summary type="html"><![CDATA[Deform 2.0 has been released. See Deform on Github. Deform is a Python form library for generating HTML forms on the server side. Date and time picking widgets, rich text editors, forms with dynamically added and removed items and a &#8230; <a href="https://opensourcehacker.com/2016/10/26/deform-2-0/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2016/10/26/deform-2-0/"><![CDATA[<p>Deform 2.0 has been released.</p>
<ul>
<li><a href="https://github.com/Pylons/deform">See Deform on Github</a>.</li>
</ul>
<p>Deform is a Python form library for generating HTML forms on the server side. <a href="http://deformdemo.repoze.org/datetimeinput/">Date and time picking widgets</a>, <a href="http://deformdemo.repoze.org/richtext/">rich text editors</a>, <a href="http://deformdemo.repoze.org/sequence_of_mappings/">forms with dynamically added and removed items</a> and a few other <a href="http://deformdemo.repoze.org/">complex use cases</a> are supported out of the box.</p>
<p>Deform integrates with the <a href="https://trypyramid.com/">Pyramid web framework</a> and several other web frameworks. Deform comes with <a href="https://chameleon.readthedocs.io/en/latest/">Chameleon templates</a> and <a href="http://getbootstrap.com">Bootstrap 3</a> styling. Under the hood, <a href="https://github.com/Pylons/colander">Colander schemas</a> are used for serialization and validation. The <a href="https://github.com/Pylons/peppercorn">Peppercorn</a> library maps HTTP form submissions to nested structure.</p>
<p>Although Deform uses Chameleon templates internally, you can embed rendered Deform forms into any template language.<a name="user-content-use-cases"></a></p>
<h2 id="Use_cases">1. Use cases</h2>
<p>Deform is ideal for complex server-side generated forms. Potential use cases include:</p>
<ul>
<li>Complex data entry forms</li>
<li>Administrative interfaces</li>
<li>Python based websites with high amount of data manipulation forms</li>
<li>Websites where additional front end framework is not needed<a name="user-content-installation"></a></li>
</ul>
<h2 id="Installation">2. Installation</h2>
<p>Install using <a href="https://packaging.python.org/en/latest/installing/">pip and Python package installation best practices</a>:</p>
<pre>pip install deform
</pre>
<h2 id="Example">3. Example</h2>
<p><a href="http://deformdemo.repoze.org">See all widget examples</a>. Below is a sample form loop using the <a href="http://trypyramid.com/">Pyramid</a> web framework.</p>
<p><a href="https://github.com/Pylons/deform/raw/master/docs/example.png" target="_blank"><img src="https://github.com/Pylons/deform/raw/master/docs/example.png" alt="https://github.com/Pylons/deform/raw/master/docs/example.png" /></a></p>
<p>Example code:</p>
<pre class="prettyprint lang-py">"""Self-contained Deform demo example."""
from __future__ import print_function

from pyramid.config import Configurator
from pyramid.session import UnencryptedCookieSessionFactoryConfig
from pyramid.httpexceptions import HTTPFound

import colander
import deform


class ExampleSchema(deform.schema.CSRFSchema):

    name = colander.SchemaNode(
        colander.String(),
        title="Name")

    age = colander.SchemaNode(
        colander.Int(),
        default=18,
        title="Age",
        description="Your age in years")


def mini_example(request):
    """Sample Deform form with validation."""

    schema = ExampleSchema().bind(request=request)

    # Create a styled button with some extra Bootstrap 3 CSS classes
    process_btn = deform.form.Button(name='process', title="Process")
    form = deform.form.Form(schema, buttons=(process_btn,))

    # User submitted this form
    if request.method == "POST":
        if 'process' in request.POST:

            try:
                appstruct = form.validate(request.POST.items())

                # Save form data from appstruct
                print("Your name:", appstruct["name"])
                print("Your age:", appstruct["age"])

                # Thank user and take him/her to the next page
                request.session.flash('Thank you for the submission.')

                # Redirect to the page shows after succesful form submission
                return HTTPFound("/")

            except deform.exception.ValidationFailure as e:
                # Render a form version where errors are visible next to the fields,
                # and the submitted values are posted back
                rendered_form = e.render()
    else:
        # Render a form with initial default values
        rendered_form = form.render()

    return {
        # This is just rendered HTML in a string
        # and can be embedded in any template language
        "rendered_form": rendered_form,
    }


def main(global_config, **settings):
    """pserve entry point"""
    session_factory = UnencryptedCookieSessionFactoryConfig('seekrit!')
    config = Configurator(settings=settings, session_factory=session_factory)
    config.include('pyramid_chameleon')
    deform.renderer.configure_zpt_renderer()
    config.add_static_view('static_deform', 'deform:static')
    config.add_route('mini_example', path='/')
    config.add_view(mini_example, route_name="mini_example", renderer="templates/mini.pt")
    return config.make_wsgi_app()</pre>
<p>This example is in <a href="http://github.com/Pylons/deformdemo/">deformdemo repository</a>. Run the example with pserve:</p>
<pre>pserve mini.ini --reload
</pre>
<h2 id="Status">4. Status</h2>
<p>This library is actively developed and maintained. Deform 2.x branch has been used in production on several sites for more than two years. Automatic test suite has 100% Python code coverage and 500+ tests.</p>
<h2 id="Projects_using_Deform">5. Projects using Deform</h2>
<ul>
<li><a href="https://websauna.org/">Websauna</a></li>
<li><a href="http://kotti.pylonsproject.org/">Kotti</a></li>
<li><a href="http://www.substanced.net/">Substance D</a></li>
</ul>
<h2 id="Community_and_links">6. Community and links</h2>
<ul>
<li><a href="http://deformdemo.repoze.org">Widget examples</a></li>
<li><a href="https://pypi.python.org/pypi/deform">PyPi</a></li>
<li><a href="http://github.com/Pylons/deform/issues">Issue tracker</a></li>
<li><a href="https://github.com/Pylons/deformdemo/">Widget examples repo</a></li>
<li><a href="http://docs.pylonsproject.org/projects/deform/en/latest/">Documentation</a></li>
<li><a href="http://www.pylonsproject.org/community/get-support">Support</a></li>
</ul>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2016/10/26/deform-2-0/#comments" thr:count="0"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2016/10/26/deform-2-0/feed/atom/" thr:count="0"/>
		<thr:total>0</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[Python standard logging pattern]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2016/05/22/python-standard-logging-pattern/" />
		<id>https://opensourcehacker.com/?p=3090</id>
		<updated>2016-05-22T19:37:58Z</updated>
		<published>2016-05-22T19:30:26Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" /><category scheme="https://opensourcehacker.com" term="logger" /><category scheme="https://opensourcehacker.com" term="logging" /><category scheme="https://opensourcehacker.com" term="pyramid" /><category scheme="https://opensourcehacker.com" term="sentry" /><category scheme="https://opensourcehacker.com" term="sqlalchemy" />		<summary type="html"><![CDATA[(this article originally appeared in Websauna documentation) 1. Introduction Python standard library provides logging module as a de facto solution for libraries and applications to log their behavior. logging is extensively used by Websauna, Pyramid, SQLAlchemy and other Python packages. &#8230; <a href="https://opensourcehacker.com/2016/05/22/python-standard-logging-pattern/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2016/05/22/python-standard-logging-pattern/"><![CDATA[<div id="introduction" class="section">
<p>(<a href="http://websauna.org/docs/narrative/local-development/logging.html">this article originally appeared in Websauna documentation</a>)</p>
<h2 id="Introduction">1. <a class="toc-backref" href="#id1">Introduction</a></h2>
<p>Python standard library provides <a class="reference external" title="(in Python v3.5)" href="http://docs.python.org/3/library/logging.html#module-logging"><code class="xref py py-mod docutils literal"><span class="pre">logging</span></code></a> module as a de facto solution for libraries and applications to log their behavior. <em>logging</em> is extensively used by <a href="https://websauna.org">Websauna</a>, <a class="reference internal" href="../../reference/glossary.html#term-pyramid"><span class="xref std std-term">Pyramid</span></a>, <a class="reference internal" href="../../reference/glossary.html#term-sqlalchemy"><span class="xref std std-term">SQLAlchemy</span></a> and other Python packages.</p>
<ul class="simple">
<li>Python logging subsystem can be configured using external configuration file and the logging configuration format is <a class="reference external" href="https://docs.python.org/3.6/library/logging.config.html#logging-config-fileformat">specified in Python standard library</a>.</li>
<li>Python logger can be individually turned on, off and their verbosity adjusted on per module basis. For example by default, Websauna development server sets SQLALchemy logging level to <code class="docutils literal"><span class="pre">INFO</span></code> instead of <code class="docutils literal"><span class="pre">DEBUG</span></code> to avoid flooding the console with verbose SQL logs. However if you are debugging issues related to a database you might want to set the SQLAlchemy logging back to <code class="docutils literal"><span class="pre">INFO</span></code>.</li>
<li>Logging is preferred diagnose method over print statements cluttered around source code.. Well designed logging calls can be left in the source code and later turned back on if the problems must be diagnosed further.</li>
<li>Python logging output can be directed to console, file, rotating file, syslog, remote server, email, etc.</li>
</ul>
</div>
<div id="log-colorization" class="section">
<h2 id="Log_colorization">2. <a class="toc-backref" href="#id2">Log colorization</a></h2>
<ul class="simple">
<li>Websauna uses <a class="reference external" href="https://github.com/laysakura/rainbow_logging_handler">rainbow_logging_handler</a> which colorizes the logs, making it easier to read them in the console of the development web server.</li>
</ul>
<p><img class="alignnone wp-image-3091 size-large" src="https://opensourcehacker.com/wp-content/uploads/2016/05/logging-1024x399.png" alt="logging" width="584" height="228" srcset="https://opensourcehacker.com/wp-content/uploads/2016/05/logging-1024x399.png 1024w, https://opensourcehacker.com/wp-content/uploads/2016/05/logging-300x117.png 300w, https://opensourcehacker.com/wp-content/uploads/2016/05/logging-768x299.png 768w, https://opensourcehacker.com/wp-content/uploads/2016/05/logging-500x195.png 500w, https://opensourcehacker.com/wp-content/uploads/2016/05/logging.png 1042w" sizes="(max-width: 584px) 100vw, 584px" /></p>
</div>
<div id="standard-logging-pattern" class="section">
<h2 id="Standard_logging_pattern">3. <a class="toc-backref" href="#id3">Standard logging pattern</a></h2>
<p>A common logging pattern in Python is:</p>
<pre class="prettify prettyprint lang-py">import logging


logger = logging.getLogger(__name__)


def my_view(request):
    logger.debug("my_view got request: %s", request)
    logger.info("my_view got request: %s", request)
    logger.error("my_view got request: %s and BAD STUFF HAPPENS", request)

    try:
        raise RuntimeError("OH NOES")
    except Exception as e:
        # Let's log full traceback even when we ignore this exception
        # and it's not risen again
        logger.exception(e)</pre>
<ul class="simple">
<li>This names a logger based on a module so you can switch logger on/off on module basis.</li>
<li>Pass logged objects to <a class="reference external" title="(in Python v3.5)" href="http://docs.python.org/3/library/logging.html#logging.Logger.debug"><code class="xref py py-meth docutils literal"><span class="pre">logging.Logger.debug()</span></code></a> and co. as full and let the logger handle the string formatting. This allows intelligent display of logged objects when using non-console logging solutions like <a class="reference internal" href="../../reference/glossary.html#term-sentry"><span class="xref std std-term">Sentry</span></a>.</li>
<li>Use <a class="reference external" title="(in Python v3.5)" href="http://docs.python.org/3/library/logging.html#logging.Logger.exception"><code class="xref py py-meth docutils literal"><span class="pre">logging.Logger.exception()</span></code></a> to report exceptions. This will record the full traceback of the exception and not just the error message.</li>
</ul>
<p>Please note that although this logging pattern is common, it’s not a universal solution. For example if you are creating third party APIs, you might want to pass the logger to a class instance of an API, so that the API consumer can take over the logger setup and there is no inversion of control.</p>
</div>
<div id="changing-logging-level-using-ini-settings" class="section">
<h2 id="Changing_logging_level_using_INI_settings">4. <a class="toc-backref" href="#id4">Changing logging level using INI settings</a></h2>
<p>Websauna defines development web server log levels in its core <a class="reference internal" href="../../reference/config.html#development-ini"><span class="std std-ref">development.ini</span></a>. Your Websauna application inherits settings from this file and can override them for each logger in the <code class="docutils literal"><span class="pre">conf/development.ini</span></code> file of your application.</p>
<p>For example to set <a class="reference internal" href="../../reference/glossary.html#term-sqlalchemy"><span class="xref std std-term">SQLAlchemy</span></a> and <code class="xref py py-mod docutils literal"><span class="pre">transaction</span></code> logging level to more verbose you can do:</p>
<div class="highlight-ini">
<pre class="highlight"><span class="k">[logger_sqlalchemy]</span>
<span class="na">level</span> <span class="o">=</span> <span class="s">DEBUG</span>

<span class="k">[logger_transaction]</span>
<span class="na">level</span> <span class="o">=</span> <span class="s">DEBUG</span>
</pre>
</div>
<p>Now console is flooded with <em>very</em> verbose logging:</p>
<div class="highlight-default">
<div class="highlight">
<pre><span class="p">[</span><span class="mi">2016</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">22</span> <span class="mi">20</span><span class="p">:</span><span class="mi">39</span><span class="p">:</span><span class="mi">55</span><span class="p">,</span><span class="mi">429</span><span class="p">]</span> <span class="p">[</span><span class="n">sqlalchemy</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">base</span><span class="o">.</span><span class="n">Engine</span> <span class="n">_begin_impl</span><span class="p">]</span> <span class="n">BEGIN</span> <span class="p">(</span><span class="n">implicit</span><span class="p">)</span>
<span class="p">[</span><span class="mi">2016</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">22</span> <span class="mi">20</span><span class="p">:</span><span class="mi">39</span><span class="p">:</span><span class="mi">55</span><span class="p">,</span><span class="mi">429</span><span class="p">]</span> <span class="p">[</span><span class="n">txn</span><span class="o">.</span><span class="mi">123145312813056</span> <span class="n">__init__</span><span class="p">]</span> <span class="n">new</span> <span class="n">transaction</span>
<span class="p">[</span><span class="mi">2016</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">22</span> <span class="mi">20</span><span class="p">:</span><span class="mi">39</span><span class="p">:</span><span class="mi">55</span><span class="p">,</span><span class="mi">429</span><span class="p">]</span> <span class="p">[</span><span class="n">sqlalchemy</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">base</span><span class="o">.</span><span class="n">Engine</span> <span class="n">_execute_context</span><span class="p">]</span> <span class="n">SELECT</span> <span class="n">users</span><span class="o">.</span><span class="n">password</span> <span class="n">AS</span> <span class="n">users_password</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">id</span> <span class="n">AS</span> <span class="n">users_id</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">uuid</span> <span class="n">AS</span> <span class="n">users_uuid</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">username</span> <span class="n">AS</span> <span class="n">users_username</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">email</span> <span class="n">AS</span> <span class="n">users_email</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">created_at</span> <span class="n">AS</span> <span class="n">users_created_at</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">updated_at</span> <span class="n">AS</span> <span class="n">users_updated_at</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">activated_at</span> <span class="n">AS</span> <span class="n">users_activated_at</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">enabled</span> <span class="n">AS</span> <span class="n">users_enabled</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">last_login_at</span> <span class="n">AS</span> <span class="n">users_last_login_at</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">last_login_ip</span> <span class="n">AS</span> <span class="n">users_last_login_ip</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">user_data</span> <span class="n">AS</span> <span class="n">users_user_data</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">last_auth_sensitive_operation_at</span> <span class="n">AS</span> <span class="n">users_last_auth_sensitive_operation_at</span><span class="p">,</span> <span class="n">users</span><span class="o">.</span><span class="n">activation_id</span> <span class="n">AS</span> <span class="n">users_activation_id</span>
</pre>
</div>
</div>
</div>
<div id="initialization-loggers-from-ini-file" class="section">
<h2 id="Initialization_loggers_from_INI_file">5. <a class="toc-backref" href="#id5">Initialization loggers from INI file</a></h2>
<p>If you need to initialize loggers in your own applications see <a class="reference internal" title="websauna.system.devop.cmdline.setup_logging" href="../../api/websauna.system.devop.cmdline.html#websauna.system.devop.cmdline.setup_logging"><code class="xref py py-func docutils literal"><span class="pre">websauna.system.devop.cmdline.setup_logging()</span></code></a> for how Websauna picks up loggers from <span class="xref std std-ref">INI</span> configuration file.</p>
</div>
<div id="more-information" class="section">
<h2 id="More_information">6. <a class="toc-backref" href="#id6">More information</a></h2>
<p><a class="reference external" href="https://github.com/websauna/websauna/blob/master/websauna/system/core/views/internalservererror.py">How Websauna logs username and email for every internal server error</a>. It’s impressive service if your devops teams calls a customer on a second an error happens and guide the customer around the error. As a bonus if using <a class="reference internal" href="../../reference/glossary.html#term-sentry"><span class="xref std std-term">Sentry</span></a> you will see the <a class="reference external" href="http://gravatar.com">Gravatar profile image</a> of the user when viewing the exception.</p>
<p><a class="reference external" href="http://pythonhosted.org/Logbook/">Logbook</a> is an alternative for Python standard library logging if performance is critical or the application has more complex logging requirements .</p>
<p><a class="reference external" href="http://reinout.vanrees.org/weblog/2015/06/05/logging-formatting.html">Discussion about log message formatting and why we are still using old style string formatting</a>.</p>
<p><a class="reference external" href="http://structlog.readthedocs.io/en/stable/index.html">structlog package</a> &#8211; add context to your logged messages like user id or HTTP request URL.</p>
</div>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2016/05/22/python-standard-logging-pattern/#comments" thr:count="0"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2016/05/22/python-standard-logging-pattern/feed/atom/" thr:count="0"/>
		<thr:total>0</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[Why source code editors cannot do beautiful soft wraps?]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2015/12/22/why-source-code-editors-cannot-do-beautiful-soft-wraps/" />
		<id>https://opensourcehacker.com/?p=3077</id>
		<updated>2015-12-22T07:35:22Z</updated>
		<published>2015-12-22T07:35:22Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" /><category scheme="https://opensourcehacker.com" term="pycharm" />		<summary type="html"><![CDATA[Again it surfaced that recommending 80 character line lengths, or any hard line lengths, might not be a good idea for the source code layout. Without making any coherent line of thought: People have different screen widths and you can &#8230; <a href="https://opensourcehacker.com/2015/12/22/why-source-code-editors-cannot-do-beautiful-soft-wraps/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2015/12/22/why-source-code-editors-cannot-do-beautiful-soft-wraps/"><![CDATA[<p>Again <a href="https://www.reddit.com/r/Python/comments/3xp4gj/dear_collaborator_pep8_is_great_but_you_have_used/">it surfaced that recommending 80 character line lengths, or any hard line lengths,</a> might not be a good idea for the source code layout.</p>
<p>Without making any coherent line of thought:</p>
<ul>
<li>People have different screen widths and you can drag to resize your window</li>
<li>My text processor was able to soft wrap sentences back in 1989. Why my source code editor cannot do the same and still retain readable code despite being 200 MB monster of Java code. Writing a logic to lay out readable code with soft word wrapping such not be such a hard task.</li>
</ul>
<p>Ugly (hardcoded line length):</p>
<p><img class="alignnone size-medium wp-image-3078" src="https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.28.26-300x144.png" alt="Screen Shot 2015-12-22 at 09.28.26" width="300" height="144" srcset="https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.28.26-300x144.png 300w, https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.28.26-768x368.png 768w, https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.28.26-1024x491.png 1024w, https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.28.26-500x240.png 500w, https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.28.26.png 1238w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>&nbsp;</p>
<p>Ugly #2 (soft wrapping is not context sensitive):</p>
<p><img class="alignnone size-medium wp-image-3079" src="https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.30.04-300x220.png" alt="Screen Shot 2015-12-22 at 09.30.04" width="300" height="220" srcset="https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.30.04-300x220.png 300w, https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.30.04-768x563.png 768w, https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.30.04-1024x750.png 1024w, https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.30.04-409x300.png 409w, https://opensourcehacker.com/wp-content/uploads/2015/12/Screen-Shot-2015-12-22-at-09.30.04.png 1108w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2015/12/22/why-source-code-editors-cannot-do-beautiful-soft-wraps/#comments" thr:count="2"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2015/12/22/why-source-code-editors-cannot-do-beautiful-soft-wraps/feed/atom/" thr:count="2"/>
		<thr:total>2</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[Twitter bot using Google Spreadsheets in Python]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2015/10/14/twitter-bot-using-google-spreadsheets-in-python/" />
		<id>https://opensourcehacker.com/?p=3069</id>
		<updated>2015-10-14T17:26:31Z</updated>
		<published>2015-10-14T17:26:31Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" /><category scheme="https://opensourcehacker.com" term="csv" /><category scheme="https://opensourcehacker.com" term="google spreadsheet" /><category scheme="https://opensourcehacker.com" term="oauth" /><category scheme="https://opensourcehacker.com" term="tweepy" /><category scheme="https://opensourcehacker.com" term="twitter" /><category scheme="https://opensourcehacker.com" term="zodb" />		<summary type="html"><![CDATA[This blog posts shows how to build a Twitter bot using Google Spreadsheets as data source in Python. The service presented here was originally created for a friend of mine who works in Megacorp Inc. They have a marketing intelligence &#8230; <a href="https://opensourcehacker.com/2015/10/14/twitter-bot-using-google-spreadsheets-in-python/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2015/10/14/twitter-bot-using-google-spreadsheets-in-python/"><![CDATA[<p>This blog posts shows how to build a Twitter bot using Google Spreadsheets as data source in Python.</p>
<p><a href="https://opensourcehacker.com/wp-content/uploads/2015/10/1-IMG_2842.jpg"><img class="size-medium wp-image-3074" src="https://opensourcehacker.com/wp-content/uploads/2015/10/1-IMG_2842-300x201.jpg" alt="Fight of lovers... they agreed and hugged at the end" width="300" height="201" srcset="https://opensourcehacker.com/wp-content/uploads/2015/10/1-IMG_2842-300x201.jpg 300w, https://opensourcehacker.com/wp-content/uploads/2015/10/1-IMG_2842-1024x686.jpg 1024w, https://opensourcehacker.com/wp-content/uploads/2015/10/1-IMG_2842-448x300.jpg 448w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>The service presented here was originally created for a friend of mine who works in Megacorp Inc. They have a marketing intelligence department that is filling out stalked information about potential customers. This information stored in Google Spreadsheet. Every day a new spreadsheet arrives to a folder. Then my friend proceeds to go through all of the leads in the spreadsheet, check who have a Twitter account and harass them in Twitter about Megacorp Inc. products.</p>
<p>To make my friend jobless I decided to replace his tedious workflow with a Python script. Python is a programming language for making simple tasks simple, eliminating the feeling of repeating yourself with as little lines as possible. So it is a good weapon of choice for crushing middle class labor force participation.</p>
<p>The bot sends two tweets to every Twitter user. Timing between tweets and the second tweet is randomized, just to make sure that no one could not figure out in a blink of an eye that they are actually communicating with a bot.</p>
<p>The ingredients of this Twitter bot are</p>
<ul>
<li><a href="https://python.org">Python 3.4+</a> &#8211; a snake programming language loved by everyone</li>
<li><a href="http://gspread.readthedocs.org/en/latest/">gspread</a> &#8211; a Python client for Google Spreadsheets making reading and manipulating data less painful</li>
<li><a href="http://tweepy.readthedocs.org/">tweepy</a> &#8211; A Twitter client library for Python</li>
<li><a href="http://zodb.readthedocs.org/en/latest/transactions.html">ZODB</a> &#8211; An ACID compliant transaction database for native Python objects</li>
</ul>
<p>The script is pretty much self-contained, around 200 lines of Python code and 3 hours of work.</p>
<h2 id="Authenticating_for_third_party_services">1. Authenticating for third party services</h2>
<p>The bot uses <a href="https://en.wikipedia.org/wiki/OAuth">OAuth</a> protocol to authenticate itself against Google services (Google Drive, Google Spreadsheet) and Twitter. In OAuth, you arrive to a service provider web site through your normal web browser. If you are not yet logged in the service asks you log in. Then you get this page where it asks authorize the app. Twitter authentication is done in a separate script run <em>tweepyauth.py</em> which asks you enter the pin number shown on Twitter website. Google API client does things different and spins up a local web server running in a localhost port. When you authorize on Google services it redirects you back to the local webserver and the script grabs the authentication token from there.</p>
<p>The script stores authentication tokens in JSON files You can run the script on your local computer first to generate JSON files and then move it to the server where a web browser for authentication is not possibly available.</p>
<h2 id="Maintaining_persistent_state">2. Maintaining persistent state</h2>
<p>The bot needs to maintain a state. It needs to process a new spreadsheet every day. But on some days the bot might not be running. Thus, it needs to remember already processed spreadsheets. Sometimes the spreadsheets may contain duplicate entries of the same Twitter handle and we don&#8217;t want to harass this Twitter user over and over again. Some data cleaning is applied to the column contents, as it might be raw Twitter handle, HTTP or HTTPS URL to a Twitter user &#8211; those marketing intelligence people are not very strict on what they spill in to their spreadsheets.</p>
<p>The state is maintained using a ZODB. ZODB is a transaction database, very robust. It is mature, probably older than some of the blog post readers, having multigigabyte deployments running factories around the world. It can run in-process like SQLite and doesn&#8217;t need other software running on the machine. It doesn&#8217;t need any ORM as it uses native Python objects. Thus, to make your application persistent you just stick your Python objects to ZODB root. Everything inside a transaction context manager is written to the disk or nothing is written to the disk.</p>
<p>As a side note using Google Spreadsheets over their REST API is painfully slow. If you need to process larger amounts of data it might be more efficient to download the data locally as CSV export and do it from there.</p>
<h2 id="Usage_instructions">3. Usage instructions</h2>
<p>This code is exemplary. You can&#8217;t use it as you do not have correct data or access to data. Use it to inspire your imagination. However if you were to use it would happen like this:</p>
<ul>
<li><a href="https://packaging.python.org/en/latest/installing.html">Follow the official Python package installation guide to install the dependencies</a></li>
<li><a href="http://apps.twitter.com/">Register Twitter app.</a> Leave callback URL empty.</li>
<li><a href="https://developers.google.com/drive/web/quickstart/python">Register a Google services App</a>.</li>
<li>Run <em>tweepyauth.py</em> to get Twitter tokens stored in <em>twitter_oauth.json</em>.</li>
<li>Run bot once on your local computer and authenticate it against Google services and write <em>client_secrets.json</em>.</li>
<li>See that the bot starts working.</li>
<li>Move the whole stuff to a server.</li>
<li>Leave it running in a loop on Bash prompt forever: $ while true; do python chirper.py; sleep</li>
</ul>
<h2 id="Source_code">4. Source code</h2>
<p><em>chirper.py</em></p>
<pre class="prettyprint lang-py">"""

Installation:

    pip install --upgrade oauth2client gspread google-api-python-client ZODB zodbpickle tweepy iso8601
"""

import time
import datetime
import json
import httplib2
import os
import sys

# Authorize server-to-server interactions from Google Compute Engine.
from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools

# ZODB
import ZODB
import ZODB.FileStorage
import BTrees.OOBTree
from persistent.mapping import PersistentMapping
import random
import transaction

# Date parsing
import iso8601

# https://github.com/burnash/gspread
import gspread

# Twitter client
import tweepy

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None


# We need permissions to drive list files, drive read files, spreadsheet manipulation
SCOPES = ['https://www.googleapis.com/auth/devstorage.read_write', 'https://www.googleapis.com/auth/drive.metadata.readonly', 'https://spreadsheets.google.com/feeds']
CLIENT_SECRET_FILE = 'client_secrets.json'
APPLICATION_NAME = 'MEGACORP SPREADSHEET SCRAPER BOT'
OAUTH_DATABASE = "oauth_authorization.json"

FIRST_TWEET_CHOICES = [
    "WE AT MEGACORP THINK YOU MIGHT LIKE US - http://megacorp.example.com",
]

SECOND_TWEET_CHOICES = [
    "AS WELL, WE ARE PROBABLY CHEAPER THAN COMPETITORCORP INC. http://megacorp.example.com/prices",
    "AS WELL, OUR FEATURE SET IS LONGER THAN MISSISSIPPI http://megacorp.example.com/features",
    "AS WELL, OUR CEO IS VERY HANDSOME http://megacorp.example.com/team",

]

# Make sure our text is edited correctly
for tweet in FIRST_TWEET_CHOICES + SECOND_TWEET_CHOICES:
    assert len(tweet) &lt; 140

# How many tweets can be send in one run... limit for testing / debugging
MAX_TWEET_COUNT = 10


# https://developers.google.com/drive/web/quickstart/python
def get_google_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """

    credential_path = os.path.join(os.getcwd(), OAUTH_DATABASE)

    store = oauth2client.file.Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatability with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials


def get_tweepy():
    """Create a Tweepy client instance."""
    creds = json.load(open("twitter_oauth.json", "rt"))

    auth = tweepy.OAuthHandler(creds["consumer_key"], creds["consumer_secret"])
    auth.set_access_token(creds["access_token"], creds["access_token_secret"])
    api = tweepy.API(auth)
    return api


def get_database():
    """Get or create a ZODB database where we store information about processed spreadsheets and sent tweets."""

    storage = ZODB.FileStorage.FileStorage('chirper.data.fs')
    db = ZODB.DB(storage)
    connection = db.open()
    root = connection.root

    # Initialize root data structure if not present yet
    with transaction.manager:
        if not hasattr(root, "files"):
            root.files = BTrees.OOBTree.BTree()
        if not hasattr(root, "twitter_handles"):
            # Format of {added: datetime, imported: datetime, sheet: str, first_tweet_at: datetime, second_tweet_at: datetime}
            root.twitter_handles = BTrees.OOBTree.BTree()


    return root


def extract_twitter_handles(spread, sheet_id, column_id="L"):
    """Process one spreadsheet and return Twitter handles in it."""

    twitter_url_prefix = ["https://twitter.com/", "http://twitter.com/"]

    worksheet = spread.open_by_key(sheet_id).sheet1

    col_index = ord(column_id) - ord("A") + 1

    # Painfully slow, 2600 records = 3+ min.
    start = time.time()
    print("Fetching data from sheet {}".format(sheet_id))
    twitter_urls =  worksheet.col_values(col_index)
    print("Fetched everything in {} seconds".format(time.time() - start))

    valid_handles = []

    # Cell contents are URLs (possibly) pointing to a Twitter
    # Extract the Twitter handle from these urls if they exist
    for cell_content in twitter_urls:

        if not cell_content:
            continue

        # Twitter handle as it
        if "://" not in cell_content:
            valid_handles.append(cell_content.strip())
            continue

        # One cell can contain multiple URLs, comma separated
        urls = [url.strip() for url in cell_content.split(",")]

        for url in urls:
            for prefix in twitter_url_prefix:
                if url.startswith(prefix):
                    handle = url[len(prefix):]

                    # Clean old style fragment URLs e.g #!/foobar
                    if handle.startswith("#!/"):
                        handle = handle[len("#!/"):]

                    valid_handles.append(handle)

    return valid_handles


def watch_files(http, title_match=None, folder_id=None) -&gt; list:
    """Check all Google Drive files which match certain file pattern.

    Drive API:

    https://developers.google.com/drive/web/search-parameters

    :return: Iterable GDrive file list
    """

    service = discovery.build('drive', 'v2', http=http)

    if folder_id:
        results = service.files().list(q="'{}' in parents".format(folder_id)).execute()
    elif title_match:
        results = service.files().list(q="title contains '{}'".format(title_match)).execute()
    else:
        raise RuntimeError("Unknown criteria")

    return results["items"]


def scan_for_new_spreadsheets(http, db):
    """Check Google Drive for new spreadsheets.

        1. Use Google Drive API to list all files matching our spreadsheet criteria
        2. If the file is not seen before add it to our list of files to process
    """
    # First discover new spreadsheets

    discovered = False

    for file in watch_files(http, folder_id="0BytechWnbrJVTlNqbGpWZllaYW8"):
        title = file["title"]
        last_char = title[-1]

        # It's .csv, photos, etc. misc files
        if not last_char.isdigit():
            continue

        with transaction.manager:
            file_id = file["id"]
            if file_id not in db.files:
                print("Discovered file {}: {}".format(file["title"], file_id))
                db.files[file_id] = PersistentMapping(file)
                discovered = True

    if not discovered:
        print("No new spreadsheets available")


def extract_twitter_handles_from_spreadsheets(spread, db):
    """Extract new Twitter handles from spreadsheets.

        1. Go through all spreadsheets we know.
        2. If the spreadsheet is not marked as processed extract Twitter handles out of it
        3. If any of the Twitter handles is unseen before add it to the database with empty record

    """

    # Then extract Twitter handles from the files we know about
    for file_id, file_data in db.files.items():

        spreadsheet_creation_date = iso8601.parse_date(file_data["createdDate"])

        print("Processing {} created at {}".format(file_data["title"], spreadsheet_creation_date))

        # Check the processing flag on the file
        if not file_data.get("processed"):
            handles = extract_twitter_handles(spread, file_id)

            # Using this transaction lock we write all the handles to the database once or none of them
            with transaction.manager:
                for handle in handles:
                    # If we have not seen this
                    if handle not in db.twitter_handles:
                        print("Importing Twitter handle {}".format(handle))
                        db.twitter_handles[handle] = PersistentMapping({"added": spreadsheet_creation_date, "imported": datetime.datetime.utcnow(), "sheet": file_id})

                file_data["processed"] = True


def send_tweet(twitter, msg):
    """Send a Tweet.
    """

    try:
        twitter.update_status(status=msg)
    except tweepy.error.TweepError as e:
        try:
            # {"errors":[{"code":187,"message":"Status is a duplicate."}]}
            resp = json.loads(e.response.text)
            if resp.get("errors"):
                if resp["errors"][0]["code"] == 187:
                    print("Was duplicate {}".format(msg))
                    time.sleep(10 + random.randint(0, 10))
                    return
        except:
            pass

        raise RuntimeError("Twitter doesn't like us: {}".format(e.response.text or str(e))) from e

    # Throttle down the bot
    time.sleep(30 + random.randint(0, 90))


def tweet_everything(twitter, db):
    """Run through all users and check if we need to Tweet to them. """

    tweet_count = 0

    for handle_id, handle_data in db.twitter_handles.items():

        with transaction.manager:

            # Check if we had not sent the first Tweet yet and send it
            if not handle_data.get("first_tweet_at"):

                tweet = "@{} {}".format(handle_id, random.choice(FIRST_TWEET_CHOICES))

                print("Tweeting {} at {}".format(tweet, datetime.datetime.utcnow()))
                send_tweet(twitter, tweet)
                handle_data["first_tweet_at"] = datetime.datetime.utcnow()
                tweet_count += 1

            # Check if we had not sent the first Tweet yet and send it
            elif not handle_data.get("second_tweet_at"):

                tweet = "@{} {}".format(handle_id, random.choice(SECOND_TWEET_CHOICES))

                print("Tweeting {} at {}".format(tweet, datetime.datetime.utcnow()))
                send_tweet(twitter, tweet)
                handle_data["second_tweet_at"] = datetime.datetime.utcnow()
                tweet_count += 1

        if tweet_count &gt;= MAX_TWEET_COUNT:
            # Testing limiter - don't spam too much if our test run is out of control
            break


def main():

    script_name = sys.argv[1] if sys.argv[0] == "python" else sys.argv[0]
    print("Starting {} at {} UTC".format(script_name, datetime.datetime.utcnow()))

    # open database
    db = get_database()

    # get OAuth permissions from Google for Drive client and Spreadsheet client
    credentials = get_google_credentials()
    http = credentials.authorize(httplib2.Http())
    spread = gspread.authorize(credentials)
    twitter = get_tweepy()

    # Do action
    scan_for_new_spreadsheets(http, db)
    extract_twitter_handles_from_spreadsheets(spread, db)
    tweet_everything(twitter, db)


main()



</pre>
<p>tweepyauth.py</p>
<pre class="prettyprint lang-py">import json
import webbrowser

import tweepy

"""
    Query the user for their consumer key/secret
    then attempt to fetch a valid access token.
"""

if __name__ == "__main__":

    consumer_key = input('Consumer key: ').strip()
    consumer_secret = input('Consumer secret: ').strip()
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)

    # Open authorization URL in browser
    webbrowser.open(auth.get_authorization_url())

    # Ask user for verifier pin
    pin = input('Verification pin number from twitter.com: ').strip()

    # Get access token
    access_token, access_token_secret = auth.get_access_token(verifier=pin)

    data = dict(consumer_key=consumer_key, consumer_secret=consumer_secret, access_token=access_token, access_token_secret=access_token_secret)
    with open("twitter_oauth.json", "wt") as f:
        json.dump(data, f)


</pre>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2015/10/14/twitter-bot-using-google-spreadsheets-in-python/#comments" thr:count="0"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2015/10/14/twitter-bot-using-google-spreadsheets-in-python/feed/atom/" thr:count="0"/>
		<thr:total>0</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[PyCharm vs. Sublime Text]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2015/05/02/pycharm-vs-sublime-text/" />
		<id>https://opensourcehacker.com/?p=3048</id>
		<updated>2015-05-02T12:44:54Z</updated>
		<published>2015-05-02T12:41:59Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" /><category scheme="https://opensourcehacker.com" term="css" /><category scheme="https://opensourcehacker.com" term="flake8" /><category scheme="https://opensourcehacker.com" term="git" /><category scheme="https://opensourcehacker.com" term="html5" /><category scheme="https://opensourcehacker.com" term="intellij" /><category scheme="https://opensourcehacker.com" term="ipython" /><category scheme="https://opensourcehacker.com" term="javascript" /><category scheme="https://opensourcehacker.com" term="pdb" /><category scheme="https://opensourcehacker.com" term="pep8" /><category scheme="https://opensourcehacker.com" term="pycharm" /><category scheme="https://opensourcehacker.com" term="sublime text" />		<summary type="html"><![CDATA[This blog post is about comparing two popular development tools and text editors, Sublime Text and PyCharm to each other. This blog post is written from the perspective of professional software development or if the programming is what you do &#8230; <a href="https://opensourcehacker.com/2015/05/02/pycharm-vs-sublime-text/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2015/05/02/pycharm-vs-sublime-text/"><![CDATA[<p>This blog post is about comparing two popular development tools and text editors, <a href="http://sublimetext.com/">Sublime Text</a> and <a href="http://www.jetbrains.com/pycharm/">PyCharm</a> to each other. This blog post is written from the perspective of professional software development or if <a href="http://programming-motherfucker.com/">the programming</a> is what you do for living.</p>
<p></p><div class='toc tableofcontent'>
   <h2>Table Of Content</h2>
   <p style='font-size:14px; line-height:14px; padding-left:0px;'><a style='color:#000000 ;' href="#Preface:_meet_the_contenders">1. Preface: meet the contenders</a></p>
<p style='font-size:14px; line-height:14px; padding-left:0px;'><a style='color:#000000 ;' href="#Feature_highlights_both_in_Sublime_Text_and_PyCharm">2. Feature highlights both in Sublime Text and PyCharm</a></p>
<p style='font-size:14px; line-height:14px; padding-left:0px;'><a style='color:#000000 ;' href="#Sublime_Text_pros">3. Sublime Text pros</a></p>
<p style='font-size:14px; line-height:14px; padding-left:0px;'><a style='color:#000000 ;' href="#PyCharm_pros">4. PyCharm pros</a></p>
<p style='font-size:14px; line-height:14px; padding-left:0px;'><a style='color:#000000 ;' href="#Conclusion_and_the_future">5. Conclusion and the future</a></p>

</div><div class='tableofcontent-end'> </div><p></p>
<h2 id="Preface:_meet_the_contenders">1. Preface: meet the contenders</h2>
<p>I have been developing Python for a decade now in various environments. Few weeks ago, I decided to make a shift from Sublime Text 3 to PyCharm as my primary tool for typing in code on OSX. I tried PyCharm long time ago and I was dissatisfied &#8211; PyCharm is built on Java software stack and UI issues, alongside &#8220;Java software bloat&#8221;, were major turn off for me by the time. But the times change, hardware gets more powerful and it was time for me to reconsider my decision.</p>
<p>Sublime Text is a commercial programmer&#8217;s text editor being in development since 2008. Its major selling points are speed, powerful code text editing features (multicursor), cross platform support, customizations and plugin ecosystem. Currently Sublime Text version 3 is in beta. Though the development slowed down in one point, as Sublime Text has been mostly one man show, <a href="http://www.sublimetext.com/3">new Sublime Text builds roll out now regularly</a>. Sublime Text costs 70 USD. Unless you purchase a license you&#8217;ll be notified by a nagging dialog.</p>
<p>PyCharm is a child of JetBrains IntelliJ IDEA family of editors. First PyCharm was released 2010, but the IDE codebase goes all way back to IntelliJ IDEA which was released as far back as 2001 &#8211; I remember doing Java development on IntelliJ in 2004. PyCharm is developed by Czech company JetBrains, having over 400 employees. PyCharm shares most of the features with other IDEA family IDEs, which means it has robust HTML, JavaScript and CSS support. PyCharm license costs 199 EUR / year (professional), 99 EUR / year (individual) and there is also <a href="https://www.jetbrains.com/pycharm/download/">free community edition</a>. The community edition is 100% open source.</p>
<p>Though Sublime Text is not an <a href="https://en.wikipedia.org/wiki/IDE">IDE</a> per se, many Python and JavaScript developers I know use it as &#8220;development platform&#8221;. This is possible because active Sublime Text community provides tools to optimize your development workflow &#8211; namely to support<a href="http://opensourcehacker.com/2014/03/10/sublime-text-3-for-python-javascript-and-web-developers/"> autocomplete, syntax highlighting and background linting</a> and various programming languages.</p>
<p>There are also other well know options for Python development, including <a href="http://pydev.org/">PyDev (LiClipse)</a>, <a href="http://komodoide.com/">Komodo IDE </a>and <a href="http://www.wingware.com/">WingWare IDE</a>.</p>
<h2 id="Feature_highlights_both_in_Sublime_Text_and_PyCharm">2. Feature highlights both in Sublime Text and PyCharm</h2>
<p>Sublime Text and PyCharm have integrated plugin manager. Sublime Text <a href="https://packagecontrol.io/">Package Control </a>is not built in, making the initial adoption more hassle. On the other hand I found PyCharm&#8217;s plugin installer to be more cumbersome to use &#8211; more clicks. Reminds me of those Windows EXE installers.</p>
<p><div id="attachment_3050" style="width: 633px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/packagecontrol.gif"><img class="wp-image-3050 size-full" src="https://opensourcehacker.com/wp-content/uploads/2015/05/packagecontrol.gif" alt="packagecontrol" width="623" height="351" /></a><p class="wp-caption-text">Installing a new plugin in Sublime Text is only few keystrokes</p></div></p>
<p><div id="attachment_3051" style="width: 310px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.30.05.png"><img class="wp-image-3051 size-medium" src="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.30.05-300x210.png" alt="Screen Shot 2015-05-02 at 14.30.05" width="300" height="210" srcset="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.30.05-300x210.png 300w, https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.30.05-1024x718.png 1024w, https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.30.05-428x300.png 428w, https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.30.05.png 1183w" sizes="(max-width: 300px) 100vw, 300px" /></a><p class="wp-caption-text">PyCharm Plugins dialog is a lot of buttons</p></div></p>
<p>Sublime text has been famous for its multicursor feature. With the release of PyCharm 4.0 it gained the multicursor support. <a href="http://stackoverflow.com/q/29719274/315168">It does not work exactly as in Sublime Text, but close enough</a>.</p>
<p>The editors enjoy plenty of themes available and both support my favorite Twilight theme. Also to further make the text more readable <a href="https://opensourcehacker.com/2012/10/07/go-pro-and-your-eyes-will-thank-you/">Source Code Pro font renders out nicely on OSX</a>.</p>
<p>The text editors are good for Python editing and have e.g. indention guidelines and fast toggle soft text wrap options.</p>
<p><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.35.08.png"><img class="alignnone size-full wp-image-3052" src="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.35.08.png" alt="Screen Shot 2015-05-02 at 14.35.08" width="204" height="187" /></a></p>
<h2 id="Sublime_Text_pros">3. Sublime Text pros</h2>
<p>Sublime Text beats PyCharm in few points and I miss these features in PyCharm, though some of them can be replaced using PyCharm alternatives.</p>
<p>Sublime Text&#8217;s <em>Go To Anywhere</em> is more powerful. Press CMD+T and type in few letters of  package and module name.  Go To Anywhere finds the suitable match. PyCharm <em>Navigate -&gt; File</em> or<em> Navigate -&gt; Symbol</em> are not as powerful as their heuristics seem to need more typing to get where you want.</p>
<p><div id="attachment_3053" style="width: 633px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/gotoanywhere.gif"><img class="wp-image-3053 size-full" src="https://opensourcehacker.com/wp-content/uploads/2015/05/gotoanywhere.gif" alt="gotoanywhere" width="623" height="351" /></a><p class="wp-caption-text">Jumping to cryptoassets.core.backend.base in Sublime Text</p></div></p>
<p>Whereas PyCharm has a scrollbar with color hints to highlight next TODO / warning / error place, Sublime Text has a minimap. Scrolling around with the minimap is more powerful as your eyes see the structure of the file unfolding.</p>
<p><div id="attachment_3054" style="width: 191px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.45.03.png"><img class="wp-image-3054 size-medium" src="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.45.03-181x300.png" alt="Screen Shot 2015-05-02 at 14.45.03" width="181" height="300" srcset="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.45.03-181x300.png 181w, https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.45.03.png 277w" sizes="(max-width: 181px) 100vw, 181px" /></a><p class="wp-caption-text">Sublime Text minimap shows outline of the file in visual</p></div></p>
<p>Sublime Text user interface is OpenGL accelerated and it runs smoothly 60 FPS all the time, making it pleasant for the eye and for typing. PyCharm is slower, though the difference is not so noticeable anymore after you pour in enough money to your hardware.</p>
<p>The Sublime Text plugin community is more vibrant. <a href="https://packagecontrol.io/browse">There are more plugins available, they get more support</a>. For example if you need to do <a href="http://deanwampler.github.io/polyglotprogramming/">polyglot programming</a> in rare languages, like <a href="http://www.kevjohnson.org/using-r-in-sublime-text-3/">R</a>, <a href="https://github.com/fjl/Sublime-Erlang">Erlang</a> or <a href="https://github.com/SublimeHaskell/SublimeHaskell">Haskell</a>, there is guaranteed to be good Sublime Text support. Also if you write documentation in Restructured Text or Markdown PyCharm did not have such good plugins as one gets for Sublime Text.</p>
<p><div id="attachment_3055" style="width: 273px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.51.32.png"><img class="size-medium wp-image-3055" src="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.51.32-263x300.png" alt="Restructured Text syntax highlighting in Sublime Text" width="263" height="300" srcset="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.51.32-263x300.png 263w, https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.51.32.png 448w" sizes="(max-width: 263px) 100vw, 263px" /></a><p class="wp-caption-text">Restructured Text syntax highlighting in Sublime Text</p></div></p>
<p>As this blog post is mostly about Python development, one cannot dismiss the fact that Sublime Text plugins are self-contained Python modules &#8211; not cumbersome Java projects. It is very easy to write them, though <a href="https://www.sublimetext.com/docs/3/api_reference.html">Sublime Text plugin API is somewhat limited</a>. There is even a menu entry <em>New plugin</em>. This might be one of the fact explaining why the Sublime Text plugin ecosystem is so healthy.</p>
<p><div id="attachment_3056" style="width: 310px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.55.48.png"><img class="wp-image-3056 size-medium" src="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.55.48-300x140.png" alt="Screen Shot 2015-05-02 at 14.55.48" width="300" height="140" srcset="https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.55.48-300x140.png 300w, https://opensourcehacker.com/wp-content/uploads/2015/05/Screen-Shot-2015-05-02-at-14.55.48.png 464w" sizes="(max-width: 300px) 100vw, 300px" /></a><p class="wp-caption-text">Creating Sublime Text plugin</p></div></p>
<h2 id="PyCharm_pros">4. PyCharm pros</h2>
<p>PyCharm is big. The editor has history since 2001, it comes tons of features out of the box. It is very polished and it does most of the features very well &#8211; after all selling IDEs is the main business for JetBrains &#8211; for example compared IBM&#8217;s Eclipse whereas IBM&#8217;s main business is sell IBM services. With PyCharm you need to spent little time to tune up your programming environment or hunt plugins for your basic development needs (Python, JavaScript, HTML, CSS).</p>
<p>PyCharm comes with an integrated debugger. You can double click to set breakpoints in your editor and then run your application to stop on the line. But you still don&#8217;t lose the ability of drop into an interactive IPython shell when hit to the breakpoint:</p>
<p><div id="attachment_3057" style="width: 914px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/pydev-debugger.gif"><img class="wp-image-3057 size-full" src="https://opensourcehacker.com/wp-content/uploads/2015/05/pydev-debugger.gif" alt="pydev debugger" width="904" height="642" /></a><p class="wp-caption-text">Dropping into IPython session after PyCharm stops in a Python breakpoint</p></div></p>
<p>Though I did find the PyCharm debugger slowing down the application too much. For example, when running a Pyramid website application inside the debugger the automatic restart cycle became too slow. You had to wait each restart more than ten seconds. This kills the basic web development flow: edit &#8211; save &#8211; refresh. Maybe there is a way to speed up the debugger for large projects &#8211; please somebody tell me?</p>
<p>Then the major reason why I switched over &#8211; due to limitations in Sublime Text plugin API one simply could not get run output where one can click Python traceback and is taken where the error happened.</p>
<p><div id="attachment_3058" style="width: 914px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/pydev-traceback.gif"><img class="wp-image-3058 size-full" src="https://opensourcehacker.com/wp-content/uploads/2015/05/pydev-traceback.gif" alt="pydev traceback" width="904" height="642" /></a><p class="wp-caption-text">Click Python traceback to navigate around the codebase and find the error root cause</p></div></p>
<p>I found this lovely navigation bar a quick fix to navigate around to related modules &#8211; partially compensates the lack of powerful Go To Anywhere as in Sublime Text:</p>
<p><div id="attachment_3059" style="width: 475px" class="wp-caption alignnone"><a href="https://opensourcehacker.com/wp-content/uploads/2015/05/navbar.gif"><img class="wp-image-3059 size-full" src="https://opensourcehacker.com/wp-content/uploads/2015/05/navbar.gif" alt="navbar" width="465" height="231" /></a><p class="wp-caption-text">PyCharm shows the path to the current file as interactive navigation bar</p></div></p>
<p>Autocomplete, autoimport and other code intel and refactoring tools work better in PyCharm. With Sublime Text you need to play around with a lot of plugins to get decent autocomplete. Sublime Text plugins have their own, incompatible settings and need a lot of manual package installation (<em>pip install flake8</em>, etc). In PyCharm I just hit<em> alt+enter</em> on a missing symbol and it got added as the import at the beginning of the file. Though I could not change it to format the import as I want &#8211; one import statement per one line.</p>
<p>PyCharm does background spellchecking of written text and Python docstrings. It&#8217;s very handy if you want to write high quality software with meaningful comments and API descriptions.</p>
<p>PyCharm has more robust integrated version control support (Git, SVN). Though Sublime Text has plugins for this, Sublime Text plugin API offers only very limited UI interaction and you cannot, for example, color files in the project explorer based on their edit status.</p>
<p>PyCharm has Power save mode. It disables background tasks like code intel which are  CPU drainage for large projects. This makes digital nomading much more fun when you are fighting over the single available power plug in a hostel on a remote island.</p>
<p>PyCharm has integrated terminal and run window, though <a href="http://stackoverflow.com/questions/29878144/pycharm-up-arrow-doesnt-work-in-the-console-on-readline-prompt">it has shortcomings and doesn&#8217;t seem to behave like a real terminal</a>.</p>
<p><a href="http://blog.jetbrains.com/pycharm/2013/07/testing-your-python-code-with-pycharm/">PyCharm has integrated unit test runner</a>. But it did not work for my py.test and splinter browser tests, as it seems to behave differently than virtualenv&#8217;ed tests launched from command line.</p>
<h2 id="Conclusion_and_the_future">5. Conclusion and the future</h2>
<p>After few weeks I found myself using PyCharm for the most of my programming needs. The key pain points PyCharm solved for me where robust code intel tools, better Python application run and debug support, with traceback clicking. The development efficiency gained from these features is enough to migrate over, even though there are features I miss in Sublime Text. However, these editors sync files perfectly and I can always alt+tab switch to Sublime Text when I need to write some Restructured Text or Markdown.</p>
<p>I am looking forward for the upcoming contender <a href="https://atom.io/">Github&#8217;s atom.io</a> editor which has the ease and flexibility of Sublime Text plugin system, but with better features, UI integration and big development-oriented company backing it up. <a href="http://blog.atom.io/2015/04/07/project-updates.html">Atom team is still working on getting the basic architecture together,</a> so it might be few years until we see robust Python tools on Atom. I&#8217;d guess HTML, CSS and JavaScript support get there sooner, as they are building the Atom itself on CoffeeScript.</p>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2015/05/02/pycharm-vs-sublime-text/#comments" thr:count="17"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2015/05/02/pycharm-vs-sublime-text/feed/atom/" thr:count="17"/>
		<thr:total>17</thr:total>
		</entry>
		<entry>
		<author>
			<name>Mikko Ohtamaa</name>
						<uri>http://opensourcehacker.com</uri>
					</author>
		<title type="html"><![CDATA[Inspecting thread dumps of hung Python processes and test runs]]></title>
		<link rel="alternate" type="text/html" href="https://opensourcehacker.com/2015/04/16/inspecting-thread-dumps-of-hung-python-processes-and-test-runs/" />
		<id>https://opensourcehacker.com/?p=3044</id>
		<updated>2015-04-16T20:39:24Z</updated>
		<published>2015-04-16T20:39:24Z</published>
		<category scheme="https://opensourcehacker.com" term="django" /><category scheme="https://opensourcehacker.com" term="plone" /><category scheme="https://opensourcehacker.com" term="python" /><category scheme="https://opensourcehacker.com" term="linux" /><category scheme="https://opensourcehacker.com" term="osx" /><category scheme="https://opensourcehacker.com" term="sqlalchemy" /><category scheme="https://opensourcehacker.com" term="thread" /><category scheme="https://opensourcehacker.com" term="unix" />		<summary type="html"><![CDATA[Sometimes, moderately complex Python applications with several threads tend to hang on exit. The application refuses to quit and just idles there waiting for something. Often this is because if any of the Python threads are alive when the process &#8230; <a href="https://opensourcehacker.com/2015/04/16/inspecting-thread-dumps-of-hung-python-processes-and-test-runs/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></summary>
		<content type="html" xml:base="https://opensourcehacker.com/2015/04/16/inspecting-thread-dumps-of-hung-python-processes-and-test-runs/"><![CDATA[<p>Sometimes, moderately complex Python applications with several threads tend to hang on exit. The application refuses to quit and just idles there waiting for something. Often this is because if any of the Python threads are alive when the process tries to exit it will wait any alive thread to terminate, unless <a href="https://docs.python.org/3/library/threading.html?highlight=thread#threading.Thread.daemon">Thread.daemon</a> is set to true.</p>
<p>In the past, it use to be little painful to figure out which thread and function causes the application to hang, but no longer! Since Python 3.3 CPython interpreter comes with a <a href="https://docs.python.org/3/library/faulthandler.html">faulthandler module</a>. faulthandler is a mechanism to tell the Python interpreter to dump the stack trace of every thread upon receiving <a href="http://en.wikipedia.org/wiki/Unix_signal">an external UNIX signal</a>.</p>
<p>Here is an example how to figure out why the unit test run, executed with <a href="pytest.org/">pytest</a>, does not exit cleanly. All tests finish, but the test suite refuses to quit.</p>
<p>First we run the tests and set a special environment variable <em>PYTHONFAULTHANDLER</em> telling CPython interpreter to activate the fault handler. This environment variable works regardless how your Python application is started (you run <em>python</em> command, you run a script directly, etc.)</p>
<pre>PYTHONFAULTHANDLER=true py.test</pre>
<p>And then the test suite has finished, printing out the last dot&#8230; but nothing happens despite our ferocious sipping of coffee.</p>
<pre>dotdotdotmoredotsthenthenthedotsstopappearing 
..</pre>
<p>How to proceed:</p>
<p><a href="http://unix.stackexchange.com/q/45025/14115">Press CTRL-Z to suspend the current active process in UNIX shell</a>.</p>
<p>Use the following command to send SIGABRT signal to the suspended process.</p>
<pre>kill -SIGABRT %1</pre>
<p>Voilá &#8211; you get the traceback. In this case, it instantly tells SQLAlchemy is waiting for something and most likely the database has deadlocked due to open conflicting transactions.</p>
<pre>Fatal Python error: Aborted

Thread 0x0000000103538000 (most recent call first):
  File "/opt/local/Library/Fra%                                                                                                                                                                     meworks/Python.framework/Versions/3.4/lib/python3.4/socketserver.py", line 154 in _eintr_retry
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/socketserver.py", line 236 in serve_forever
  File "/Users/mikko/code/trees/pyramid_web20/pyramid_web20/tests/functional.py", line 40 in run
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 921 in _bootstrap_inner
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 889 in _bootstrap

Current thread 0x00007fff75128310 (most recent call first):
  File "/Users/mikko/code/trees/venv/lib/python3.4/site-packages/SQLAlchemy-1.0.0b5-py3.4-macosx-10.9-x86_64.egg/sqlalchemy/engine/default.py", line 442 in do_execute
...
  File "/Users/mikko/code/trees/venv/lib/python3.4/site-packages/SQLAlchemy-1.0.0b5-py3.4-macosx-10.9-x86_64.egg/sqlalchemy/sql/schema.py", line 3638 in drop_all
  File "/Users/mikko/code/trees/pyramid_web20/pyramid_web20/tests/conftest.py", line 124 in teardown
...
  File "/Users/mikko/code/trees/venv/lib/python3.4/site-packages/_pytest/config.py", line 41 in main
  File "/Users/mikko/code/trees/venv/bin/py.test", line 9 in &lt;module&gt;</pre>
<p class=\"signature\">
 <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\"><img valign=\"middle\" src=\"https://www.feedburner.com/fb/images/pub/feed-icon16x16.png\" alt=\"\" style=\"border:0\"/></a> <a href=\"https://feeds.feedburner.com/OpenSourceHacker\" rel=\"alternate\" type=\"application/rss+xml\">Subscribe to RSS feed</a> <a href=\"https://twitter.com/moo9000\"> <img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/twitter-24.png\"></a> <a href=\"https://twitter.com/moo9000\">Follow me on Twitter</a> 
 <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\"> <img valign=\"middle\"  style=\"border:0\" src=\"http://opensourcehacker.com/wp-content/uploads/facebook-24.png\"></a> <a href=\"https://www.facebook.com/pages/Open-Source-Hacker/181710458567630\">Follow me on Facebook</a> <a href=\"https://plus.google.com/103323677227728078543/\"><img valign=\"middle\"  style=\"border:0\" src=\"https://opensourcehacker.com/wp-content/uploads/googleplus.png\"></a> <a href=\"https://plus.google.com/103323677227728078543/\">Follow me Google+</a></p>
]]></content>
			<link rel="replies" type="text/html" href="https://opensourcehacker.com/2015/04/16/inspecting-thread-dumps-of-hung-python-processes-and-test-runs/#comments" thr:count="3"/>
		<link rel="replies" type="application/atom+xml" href="https://opensourcehacker.com/2015/04/16/inspecting-thread-dumps-of-hung-python-processes-and-test-runs/feed/atom/" thr:count="3"/>
		<thr:total>3</thr:total>
		</entry>
	</feed>
