Compare commits

...

5 Commits

Author SHA1 Message Date
Ryan Rix 9992ed7e9b narrow dependency on arrow 2024-02-12 10:45:37 -08:00
Ryan Rix 9102faebe8 a few more tests 2024-02-12 10:45:24 -08:00
Ryan Rix 903ee9b4c9 fix PYTHONPATH for shell env 2024-02-08 08:30:55 -08:00
Ryan Rix d3e732bb63 start to add unit tests for roam models 2024-02-08 08:30:44 -08:00
Ryan Rix 127c34838f write up a test plan 2024-02-06 14:04:34 -08:00
8 changed files with 495 additions and 14 deletions

View File

@ -47,13 +47,16 @@ This project is a living document. It's [[id:cce/literate_programming][Literate
- [[id:arroyo/django/generators][The Arroyo Generators]] are the "API" which can be used to build a [[id:60f710b2-6a1f-44be-bc13-dfe01e46d4e3][Concept Operating System]] using literate programming in org-mode.
* Rough Timeline and Task List
:PROPERTIES:
:ID: 20240205T101753.548048
:END:
- tests, unit tests, functional tests with org-documents, eugh.
- Prometheus and basic page hit count/analytics
- Deployment and NixOS module to testing-subdomains
- Sitemap
- Move [[id:arroyo/django/generators][The Arroyo Generators]] and perhaps [[id:arcology/django/roam][The Arcology Roam Models]] in to [[id:arroyo/arroyo][Arroyo Systems Management]]
- [[id:20231113T195508.942155][Rebuild of The Complete Computer]]
- Consider having a second sqlite3 with server-written state like hit counts and fedi urls and whatnot that i am nervous to store in a DB right now.
- tests, unit tests, functional tests with org-documents, eugh.
* [[id:20220116T143655.499306][Hey Smell This]]

View File

@ -808,6 +808,7 @@ An Atom feed is pretty simple, it's an XML document with multiple =<entry>='s an
</feed>
#+end_src
*** NEXT add category/tags to the entries
*** NEXT move this function to somewhere else more reasonable
This template relies on this custom Django template i [[https://stackoverflow.com/questions/8000022/django-template-how-to-look-up-a-dictionary-value-with-a-variable][nicked from StackOverflow]] to access a dict with a variable key.
@ -1101,3 +1102,22 @@ def site_css(request):
''')
return HttpResponse(stanzas, content_type="text/css")
#+end_src
* NEXT Testing
- site from_request and from_key need to be tested
- site urlize page function needs to be tested too
- page collect functions at least need type annotations...
- =to_html= instance method needs to be tested (and the memoization too)
- =create_from_arroyo= too
- =feed= and =feedentry=
- both the =create_from_arroyo=, =to_html=
- the feed generator stuff in the view probably should go in to a model class, but test it.
- page handler view logic, test that 404s work, check that localhost loads work
- check optional sidebar stuff in the view logic
- sitemap when i write it
- per-site link color css endpoint

View File

@ -12,7 +12,7 @@ talk about what these modules are used to do, show the management commands from
* Arroyo Generator Data Models
document the Generator pattern described by the Abstract Base Class.
** NEXT document the Generator pattern described by the Abstract Base Class.
#+begin_src python :tangle generators/models.py
from __future__ import annotations
@ -119,9 +119,15 @@ class Generator(models.Model):
return ret
#+end_src
*** NEXT Testing
Test the classmethods =create_from_arroyo= and =collect_for_babel= and the =to_babel= instance method
role list and exclusions in =create_from_arroyo=
** Generate an Emacs configuration
Emacs configuration is not declarative but imperative so dependency order needs to be established. =#+ARROYO_MODULE_WANTS= and =#+ARROYO_MODULE_WANTED= allow documents to describe dependency relationships. The python built-in =graphlib= is used to order the =EmacsSnippets=, with the =DependencyRelationship= defining edges in the snippets graph.
Emacs configuration is not declarative but imperative so dependency order needs to be established which the Nix modules do not nee to perform. =#+ARROYO_MODULE_WANTS= and =#+ARROYO_MODULE_WANTED= Keywords allow documents to describe dependency relationships. The python built-in [[https://docs.python.org/3/library/graphlib.html][=graphlib=]] is used to order the =EmacsSnippets=, with the =DependencyRelationship= class caching edges between the snippets in the database.
#+begin_src python :tangle generators/models.py
class DependencyRelationship(models.Model):
@ -240,6 +246,12 @@ class EmacsSnippet(Generator):
return new_snippets
#+end_src
*** NEXT Testing
Test the classmethods and =to_babel= instance method
test the depdendencyrelationship stuff, the graphlib sort.
** Emacs custom package overrides
It's possible for pages to stash Nix code snippets in to tangled files to make custom Emacs packages available to Arroyo Emacs using helpers like nixpkgs's =melpaBuild= and =trivialBuild= commands.
@ -262,6 +274,10 @@ class EmacsEpkg(Generator):
return self._to_babel_inclusion(formatter=formatter)
#+end_src
*** NEXT Testing
this one is pretty straightforward implementation of the ABC.
** Declarative NixOS module imports
It's possible for pages to signal all or certain deployment roles to include a new NixOS module. This allows a user to download an org-mode file in to their org-roam directory and it will and new functionality to their computer.
@ -285,6 +301,10 @@ class NixosModule(Generator):
return self._to_babel_list(formatter=formatter)
#+end_src
*** NEXT Testing
this one is pretty straightforward implementation of the ABC.
** Declarative =home-manager= module imports
It's possible to add home-manager modules to the user's profile in much the same way.
@ -307,6 +327,10 @@ class HomeManagerModule(Generator):
return self._to_babel_list(formatter=formatter)
#+end_src
*** NEXT Testing
this one is pretty straightforward implementation of the ABC.
** NEXT need to squash these
#+begin_src python :tangle generators/migrations/__init__.py
@ -677,6 +701,8 @@ class RoleAdmin(admin.ModelAdmin):
* NEXT public site views to show information about the [[id:cce/cce][The Complete Computing Environment]] built around this arcology.
it would be nice to generate the tables of contents pages or subsections from the DB, for example...
#+begin_src python :tangle generators/views.py
from django.shortcuts import render

268
roam.org
View File

@ -1,3 +1,4 @@
# -*- org-src-preserve-indentation: t; -*-
:PROPERTIES:
:ID: arcology/django/roam
:END:
@ -66,6 +67,61 @@ class File(models.Model):
)[0]
#+END_SRC
*** =roam.models.File= Testing
#+begin_src python :tangle roam/tests.py
from django.test import TestCase
from django.db.utils import IntegrityError
from roam.models import File
from django.conf import settings
from arroyo import parse_file
import subprocess
#+end_src
#+begin_src python :tangle roam/tests.py
from roam.models import File
class RoamFileTest(TestCase):
def setUp(self):
# super().setUp()
self.native = parse_file(str(settings.BASE_DIR / "./README.org"))
self.expected_path = str(settings.BASE_DIR / "./README.org")
self.expected_hash = (
subprocess.check_output(
f"sha256sum {self.expected_path} | awk '{{print $1}}'", shell=True
)
.decode("UTF-8")
.rstrip()
)
#+end_src
test =create_from_arroyo=, parse this document and see if we can get a =File= out of it lul
#+begin_src python :tangle roam/tests.py
def test_cfa(self):
File.create_from_arroyo(self.native)
obj = File.objects.first()
# ensure object is instantiated properly
self.assertEqual(obj.path, self.expected_path)
self.assertEqual(obj.digest, self.expected_hash)
#+end_src
test the =hash_updated= function, synthesize a File object and check the behavior of =hash_updated= and that =calculate_hash= works
#+begin_src python :tangle roam/tests.py
def test_hash_updated(self):
t_file = File(
path=self.expected_path,
digest="12345"
)
self.assertNotEqual(t_file.digest, self.expected_hash)
self.assertEqual(t_file.hash_updated(), True)
t_file.digest = self.expected_hash
self.assertEqual(t_file.hash_updated(), False)
#+end_src
** Keyword
#+BEGIN_SRC python :tangle roam/models.py
@ -100,6 +156,78 @@ class Keyword(models.Model):
]
#+END_SRC
*** =roam.models.Keyword= Testing
- test the =path= =ForeignKey=
- test common queries from elsewhere in the codebase (and probably slurp those in to instance methods along the way)
#+begin_src python :tangle roam/tests.py
from roam.models import Keyword
class RoamKeywordTest(TestCase):
def setUp(self):
# super().setUp()
self.native = parse_file(str(settings.BASE_DIR / "./README.org"))
self.expected_path = str(settings.BASE_DIR / "./README.org")
#+end_src
- test =create_from_arroyo=
- parse this document and see if we can get a list of =Keyword= out of it lul
- validate that =ROAM_ALLOW_KEYWORDS= filter works
#+begin_src python :tangle roam/tests.py
def test_cfa(self):
file = File.create_from_arroyo(self.native)
kws = Keyword.create_from_arroyo(self.native)
self.assertEqual(len(kws), len(Keyword.objects.all()))
self.assertEqual(kws[0].keyword, "ARCOLOGY_KEY")
self.assertEqual(kws[0].value, "arcology/django")
self.assertEqual(kws[0].path, file)
self.assertEqual(kws[0].path.path, self.expected_path)
#+end_src
- =ROAM_ALLOWED_KEYWORDS= filtering
#+begin_src python :tangle roam/tests.py
def test_cfa_allowed_keywords(self):
_file = File.create_from_arroyo(self.native)
kws = Keyword.create_from_arroyo(self.native)
kws_map = map(lambda kw: kw.keyword, kws)
self.assertNotIn("FILETAGS", kws_map)
#+end_src
- =Keyword.Meta.uniqeuness= testing
#+begin_src python :tangle roam/tests.py
def test_uniqueness(self):
file = File.create_from_arroyo(self.native)
kw1 = Keyword(
path=file,
keyword="WHICH_ONE",
value="THE_FIRST",
)
kw2 = Keyword(
path=file,
keyword="WHICH_ONE",
value="THE_SECOND",
)
kw3 = Keyword(
path=file,
keyword="WHICH_ONE",
value="THE_FIRST",
)
kw1.save() # these will work
kw2.save() # these will work
with self.assertRaises(IntegrityError):
kw3.save() # this will raise because of the uniqeuness check
#+end_src
** Heading
#+BEGIN_SRC python :tangle roam/models.py
@ -145,6 +273,61 @@ class Heading(models.Model):
]
#+END_SRC
*** =roam.models.Heading= Testing
- synthesize a heading set
- check =inbound_headings= and =outbound_headings= (do i even use this, should i even use this?)
- check =to_url=, this is weird because it relies on =File.page_set()= which is an implicit dependency on an Arcology model. 😳
#+begin_src python :tangle roam/tests.py
from roam.models import Heading
class RoamHeadingTest(TestCase):
def setUp(self):
self.native = parse_file(str(settings.BASE_DIR / "./README.org"))
self.expected_path = str(settings.BASE_DIR / "./README.org")
self.file = File.create_from_arroyo(self.native)
#+end_src
Test =create_from_arroyo= on this document:
#+begin_src python :tangle roam/tests.py
def test_create_create_from_arroyo(self):
headings = Heading.create_from_arroyo(self.native)
# only headings with IDs will be created; this may need to be changed later on
README_ID_HEADING_CNT = 2
self.assertEqual(len(headings), README_ID_HEADING_CNT)
#+end_src
Test that file relationships are created:
#+begin_src python :tangle roam/tests.py
def test_heading_relationships(self):
headings = Heading.create_from_arroyo(self.native)
for heading in headings:
self.assertEquals(heading.path, self.file)
#+end_src
Test that object internals are set properly:
#+begin_src python :tangle roam/tests.py
def test_object_internals(self):
headings = Heading.create_from_arroyo(self.native)
self.assertEquals(headings[0].level, 0)
self.assertEquals(headings[0].node_id, "arcology/django/readme")
self.assertEquals(headings[0].title, "The Arcology Project: Django Edition")
#+end_src
I need to create a Page and a Site to test this ... weird concern-separation happening here.
#+begin_src python :tangle roam/tests.py
def test_to_url(self):
pass
# raise Exception("Not implemented!")
#+end_src
** Properties
#+begin_src python :tangle roam/models.py
@ -170,6 +353,56 @@ class HeadingProperty(models.Model):
]
#+end_src
*** NEXT =roam.models.HeadingProperty= Testing
#+begin_src python :tangle roam/tests.py
from roam.models import HeadingProperty
class RoamHeadingPropertyTest(TestCase):
def setUp(self):
# super().setUp()
self.native = parse_file(str(settings.BASE_DIR / "./README.org"))
self.expected_path = str(settings.BASE_DIR / "./README.org")
#+end_src
- test =create_from_arroyo=, parse this doc, create file and headings, and properties, validate properties are populated properly
- including top-level file-properties (this will fail right now, i think)
#+begin_src python :tangle roam/tests.py
def test_cfa(self):
_file = File.create_from_arroyo(self.native)
headings = Heading.create_from_arroyo(self.native)
props = HeadingProperty.create_from_arroyo(self.native)
#+end_src
this will raise because level 0 file properties are not persisted, I need to fix it in [[id:20231023T115950.248543][arroyo_rs]]. It's not included in the test, but I'd like to be able to once I fix arroyo_rs.
#+begin_src python
# self.assertEquals(len(props), 2)
#+end_src
fetch a level 0 heading and test it. This will also fail and is not included in the test
#+begin_src python
l0_heading = next(filter(lambda h: h.level == 0, headings))
self.assertEquals(l0_heading.level, 0)
l0h_properties = l0_heading.headingproperty_set.all()
# self.assertNotEquals(len(l0h_properties), 0)
#+end_src
Level 1 headings will be properly persisted, let's see if the ID for [[id:20240205T101753.548048][Rough Timeline and Task List]] is populated.
#+begin_src python :tangle roam/tests.py
l1_heading = next(filter(lambda h: h.title == "Rough Timeline and Task List", headings))
l1h_properties = l1_heading.headingproperty_set.all()
self.assertEquals(len(l1h_properties), 1)
self.assertEquals(l1h_properties[0].keyword, "ID")
self.assertEquals(l1h_properties[0].value, "20240205T101753.548048")
#+end_src
**** NEXT [#A] fix file-level property drawer extraction in [[id:20231023T115950.248543][arroyo_rs]], enable level 0 tests
** Tag
#+BEGIN_SRC python :tangle roam/models.py
@ -196,6 +429,13 @@ class Tag(models.Model):
]
#+END_SRC
*** NEXT Testing
- test =create_from_arroyo=
- parse this doc, create file and headings, and tags, check some tags from this document
- including top-level file-properties and filetags
- check and audit queries, consider making more instance methods
** Reference
#+BEGIN_SRC python :tangle roam/models.py
@ -219,6 +459,13 @@ class Reference(models.Model):
]
#+END_SRC
*** NEXT Testing
- test =create_from_arroyo=
- parse this doc, create file and headings, and tags, check some refs from this document
- including top-level file properties refs
- check and audit queries, consider making more instance methods
** Link
#+BEGIN_SRC python :tangle roam/models.py
@ -292,6 +539,15 @@ class Link(models.Model):
return ret
#+END_SRC
*** NEXT Testing
- test =create_from_arroyo=
- parse this doc, create file and headings, and validate the behavior of links through ones on this page
- internal ID links, external HTTP/s, shell commands and other Emacs clickables
- check and audit queries, consider making more instance methods
** NEXT Roam Heading Aliases
* Parsing and Persisting an org-mode document
:PROPERTIES:
:ID: 20231218T151642.210449
@ -403,6 +659,12 @@ def parse_doc(path: str) -> native.Document:
return native.parse_file(path)
#+end_src
** NEXT Testing
Test =is_file_updated= and =should_file_persist=, these are scary.
Test parse_doc and the cache behavior somehow, that =XXX= above...
* NEXT split up the migration
#+BEGIN_SRC python :tangle roam/migrations/0001_base.py :noweb yes
@ -605,11 +867,7 @@ class Migration(migrations.Migration):
* NEXT tests
#+begin_src python :tangle roam/tests.py
from django.test import TestCase
# Create your tests here.
#+end_src
** NEXT create a test harness for =create_from_arroyo= tests...
* Admin

View File

@ -1,4 +1,4 @@
# [[file:../roam.org::*Models][Models:1]]
# [[file:../roam.org::*Org-Roam Caching Models][Org-Roam Caching Models:1]]
from __future__ import annotations
import hashlib
from typing import List
@ -12,7 +12,7 @@ import arroyo.arroyo_rs as native
import logging
logger = logging.getLogger(__name__)
# Models:1 ends here
# Org-Roam Caching Models:1 ends here
# [[file:../roam.org::*File][File:1]]
def calculate_hash(path: str) -> str:

View File

@ -1,5 +1,173 @@
# [[file:../roam.org::*tests][tests:1]]
# [[file:../roam.org::*=roam.models.File= Testing][=roam.models.File= Testing:1]]
from django.test import TestCase
from django.db.utils import IntegrityError
# Create your tests here.
# tests:1 ends here
from roam.models import File
from django.conf import settings
from arroyo import parse_file
import subprocess
# =roam.models.File= Testing:1 ends here
# [[file:../roam.org::*=roam.models.File= Testing][=roam.models.File= Testing:2]]
from roam.models import File
class RoamFileTest(TestCase):
def setUp(self):
# super().setUp()
self.native = parse_file(str(settings.BASE_DIR / "./README.org"))
self.expected_path = str(settings.BASE_DIR / "./README.org")
self.expected_hash = (
subprocess.check_output(
f"sha256sum {self.expected_path} | awk '{{print $1}}'", shell=True
)
.decode("UTF-8")
.rstrip()
)
# =roam.models.File= Testing:2 ends here
# [[file:../roam.org::*=roam.models.File= Testing][=roam.models.File= Testing:3]]
def test_cfa(self):
File.create_from_arroyo(self.native)
obj = File.objects.first()
# ensure object is instantiated properly
self.assertEqual(obj.path, self.expected_path)
self.assertEqual(obj.digest, self.expected_hash)
# =roam.models.File= Testing:3 ends here
# [[file:../roam.org::*=roam.models.File= Testing][=roam.models.File= Testing:4]]
def test_hash_updated(self):
t_file = File(
path=self.expected_path,
digest="12345"
)
self.assertNotEqual(t_file.digest, self.expected_hash)
self.assertEqual(t_file.hash_updated(), True)
t_file.digest = self.expected_hash
self.assertEqual(t_file.hash_updated(), False)
# =roam.models.File= Testing:4 ends here
# [[file:../roam.org::*=roam.models.Keyword= Testing][=roam.models.Keyword= Testing:1]]
from roam.models import Keyword
class RoamKeywordTest(TestCase):
def setUp(self):
# super().setUp()
self.native = parse_file(str(settings.BASE_DIR / "./README.org"))
self.expected_path = str(settings.BASE_DIR / "./README.org")
# =roam.models.Keyword= Testing:1 ends here
# [[file:../roam.org::*=roam.models.Keyword= Testing][=roam.models.Keyword= Testing:2]]
def test_cfa(self):
file = File.create_from_arroyo(self.native)
kws = Keyword.create_from_arroyo(self.native)
self.assertEqual(len(kws), len(Keyword.objects.all()))
self.assertEqual(kws[0].keyword, "ARCOLOGY_KEY")
self.assertEqual(kws[0].value, "arcology/django")
self.assertEqual(kws[0].path, file)
self.assertEqual(kws[0].path.path, self.expected_path)
# =roam.models.Keyword= Testing:2 ends here
# [[file:../roam.org::*=roam.models.Keyword= Testing][=roam.models.Keyword= Testing:3]]
def test_cfa_allowed_keywords(self):
_file = File.create_from_arroyo(self.native)
kws = Keyword.create_from_arroyo(self.native)
kws_map = map(lambda kw: kw.keyword, kws)
self.assertNotIn("FILETAGS", kws_map)
# =roam.models.Keyword= Testing:3 ends here
# [[file:../roam.org::*=roam.models.Keyword= Testing][=roam.models.Keyword= Testing:4]]
def test_uniqueness(self):
file = File.create_from_arroyo(self.native)
kw1 = Keyword(
path=file,
keyword="WHICH_ONE",
value="THE_FIRST",
)
kw2 = Keyword(
path=file,
keyword="WHICH_ONE",
value="THE_SECOND",
)
kw3 = Keyword(
path=file,
keyword="WHICH_ONE",
value="THE_FIRST",
)
kw1.save() # these will work
kw2.save() # these will work
with self.assertRaises(IntegrityError):
kw3.save() # this will raise because of the uniqeuness check
# =roam.models.Keyword= Testing:4 ends here
# [[file:../roam.org::*=roam.models.Heading= Testing][=roam.models.Heading= Testing:1]]
from roam.models import Heading
class RoamHeadingTest(TestCase):
def setUp(self):
self.native = parse_file(str(settings.BASE_DIR / "./README.org"))
self.expected_path = str(settings.BASE_DIR / "./README.org")
self.file = File.create_from_arroyo(self.native)
# =roam.models.Heading= Testing:1 ends here
# [[file:../roam.org::*=roam.models.Heading= Testing][=roam.models.Heading= Testing:2]]
def test_create_create_from_arroyo(self):
headings = Heading.create_from_arroyo(self.native)
# only headings with IDs will be created; this may need to be changed later on
README_ID_HEADING_CNT = 2
self.assertEqual(len(headings), README_ID_HEADING_CNT)
# =roam.models.Heading= Testing:2 ends here
# [[file:../roam.org::*=roam.models.Heading= Testing][=roam.models.Heading= Testing:3]]
def test_heading_relationships(self):
headings = Heading.create_from_arroyo(self.native)
for heading in headings:
self.assertEquals(heading.path, self.file)
# =roam.models.Heading= Testing:3 ends here
# [[file:../roam.org::*=roam.models.Heading= Testing][=roam.models.Heading= Testing:4]]
def test_object_internals(self):
headings = Heading.create_from_arroyo(self.native)
self.assertEquals(headings[0].level, 0)
self.assertEquals(headings[0].node_id, "arcology/django/readme")
self.assertEquals(headings[0].title, "The Arcology Project: Django Edition")
# =roam.models.Heading= Testing:4 ends here
# [[file:../roam.org::*=roam.models.Heading= Testing][=roam.models.Heading= Testing:5]]
def test_to_url(self):
pass
# raise Exception("Not implemented!")
# =roam.models.Heading= Testing:5 ends here
# [[file:../roam.org::*=roam.models.HeadingProperty= Testing][=roam.models.HeadingProperty= Testing:1]]
from roam.models import HeadingProperty
class RoamHeadingPropertyTest(TestCase):
def setUp(self):
# super().setUp()
self.native = parse_file(str(settings.BASE_DIR / "./README.org"))
self.expected_path = str(settings.BASE_DIR / "./README.org")
# =roam.models.HeadingProperty= Testing:1 ends here
# [[file:../roam.org::*=roam.models.HeadingProperty= Testing][=roam.models.HeadingProperty= Testing:2]]
def test_cfa(self):
_file = File.create_from_arroyo(self.native)
headings = Heading.create_from_arroyo(self.native)
props = HeadingProperty.create_from_arroyo(self.native)
# =roam.models.HeadingProperty= Testing:2 ends here
# [[file:../roam.org::*=roam.models.HeadingProperty= Testing][=roam.models.HeadingProperty= Testing:5]]
l1_heading = next(filter(lambda h: h.title == "Rough Timeline and Task List", headings))
l1h_properties = l1_heading.headingproperty_set.all()
self.assertEquals(len(l1h_properties), 1)
self.assertEquals(l1h_properties[0].keyword, "ID")
self.assertEquals(l1h_properties[0].value, "20240205T101753.548048")
# =roam.models.HeadingProperty= Testing:5 ends here

View File

@ -17,7 +17,7 @@ version = "0.0.1"
description = "org-mode metadata query engine, publishing platform, and computer metaprogrammer"
# license = "Hey Smell This"
readme = "README.md"
dependencies = ["click ~=8.1", "django ~= 4.2", "django-stub", "polling", "arroyo", "arrow"]
dependencies = ["click ~=8.1", "django ~= 4.2", "django-stub", "polling", "arroyo", "arrow ~= 1.3.0"]
requires-python = ">=3.10"
authors = [
{ name = "Ryan Rix", email = "code@whatthefuck.computer" }
@ -158,6 +158,9 @@ in pkgs.mkShell {
black]);
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
NIX_CONFIG = "builders =";
shellHook = ''
PYTHONPATH=${myPython}/${myPython.sitePackages}
'';
}
#+end_src

View File

@ -26,5 +26,8 @@ in pkgs.mkShell {
black]);
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
NIX_CONFIG = "builders =";
shellHook = ''
PYTHONPATH=${myPython}/${myPython.sitePackages}
'';
}
# Dev Environment:1 ends here