Browse Source

initial work on making a cas importing system...

main
John-Mark Gurney 9 months ago
commit
de3e3f8dc8
7 changed files with 154 additions and 0 deletions
  1. +4
    -0
      .gitignore
  2. +9
    -0
      Makefile
  3. +19
    -0
      README.md
  4. +85
    -0
      casimport/__init__.py
  5. +9
    -0
      fixtures/hello.py
  6. +4
    -0
      requirements.txt
  7. +24
    -0
      setup.py

+ 4
- 0
.gitignore View File

@@ -0,0 +1,4 @@
.coverage
*.egg-info
p
*.pyc

+ 9
- 0
Makefile View File

@@ -0,0 +1,9 @@
MODULES=casimport

VIRTUALENV?=virtualenv-3.7

test:
(ls $(MODULES)/*.py | entr sh -c 'python -m coverage run -m unittest $(basename $(MODULES)) && coverage report --omit=p/\* -m -i')

env:
($(VIRTUALENV) p && . ./p/bin/activate && pip install -r requirements.txt)

+ 19
- 0
README.md View File

@@ -0,0 +1,19 @@
casimport
=========

Usage
-----

```
import casimport
from cas.v1_f_330884aa2febb5e19fb7194ec6a69ed11dd3d77122f1a5175ee93e73cf0161c3 import hello

print(repr(hello('Alice')))
```

Defintion of hash:
v<num>_<type>_<hashvalue>

Currently v1 is defined, and has the following types:
f The hash is the value of the Python source file.
Generated via: shasum -a 256 hello.py

+ 85
- 0
casimport/__init__.py View File

@@ -0,0 +1,85 @@
import glob
import hashlib
import os.path
import sys

from importlib.abc import MetaPathFinder, Loader
from importlib.machinery import ModuleSpec

class FileDirCAS(object):
def __init__(self, path):
self._path = path
self._hashes = {}

for i in glob.glob(os.path.join(path, '*.py')):
_, hash = self.read_hash_file(i)
self._hashes[hash] = i

def read_hash_file(self, fname):
with open(fname, 'rb') as fp:
data = fp.read()
hash = hashlib.sha256(data).hexdigest()

return data, hash

def is_package(self, hash):
return False

def exec_module(self, hash, module):
parts = hash.split('_', 2)
fname = self._hashes[parts[2]]

data, fhash = self.read_hash_file(fname)

if fhash != parts[2]:
raise ValueError('file no longer matches hash on disk')

exec(data, module.__dict__)

class CASFinder(MetaPathFinder, Loader):
def __init__(self):
self._loaders = []

sys.meta_path.append(self)

def register(self, loader):
self._loaders.append(loader)

# MetaPathFinder methods
def find_spec(self, fullname, path, target=None):
if path is None:
ms = ModuleSpec(fullname, self, is_package=True)
else:
parts = fullname.split('.')
for l in self._loaders:
ispkg = l.is_package(parts[1])
break

ms = ModuleSpec(fullname, self, is_package=True, loader_state=(parts[1], l))

return ms

def invalidate_caches(self):
return None

# Loader methods
def exec_module(self, module):
if module.__name__ == 'cas':
pass
else:
hash, load = module.__spec__.loader_state
load.exec_module(hash, module)

import unittest

class Test(unittest.TestCase):
def test_casimport(self):
f = CASFinder()
f.register(FileDirCAS(os.path.join(os.path.dirname(__file__), '..', 'fixtures')))

import cas

from cas.v1_f_330884aa2febb5e19fb7194ec6a69ed11dd3d77122f1a5175ee93e73cf0161c3 import hello

name = 'Olof'
self.assertEqual(hello(name), 'hello ' + name)

+ 9
- 0
fixtures/hello.py View File

@@ -0,0 +1,9 @@
def hello(name):
return 'hello %s' % name

import unittest

class Tests(unittest.TestCase):
def test_hello(self):
self.assertEqual(hello('sam'), 'hello sam')
self.assertEqual(hello('bob'), 'hello bob')

+ 4
- 0
requirements.txt View File

@@ -0,0 +1,4 @@
# use setup.py for dependancy info
-e .

-e .[dev]

+ 24
- 0
setup.py View File

@@ -0,0 +1,24 @@
#!/usr/bin/env python

from setuptools import setup

setup(name='casimport',
version='0.1.0',
description='Import python modules via content address.',
author='John-Mark Gurney',
author_email='jmg@funkthat.com',
classifiers=[
'Development Status :: 3 - Alpha',
'License :: OSI Approved :: BSD License',
'Topic :: Security',
# XXX - add
],
url='https://www.funkthat.com/gitea/jmg/casimport',
packages=[ 'casimport', ],
install_requires=[
'mock',
],
extras_require = {
'dev': [ 'coverage' ],
}
)

Loading…
Cancel
Save