Skip to content
This repository was archived by the owner on Sep 21, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PYTHON := python2
VENV := .venv
VENV ?= .venv

# autopep8 uses pycodestyle but doesn't automatically find files the same way :-/
REFORMAT := ensime_shared/ rplugin/
Expand All @@ -8,9 +8,9 @@ activate := $(VENV)/bin/activate
requirements := requirements.txt test-requirements.txt
deps := $(VENV)/deps-updated

test: $(deps)
@echo "Running ensime-vim lettuce tests"
. $(activate) && lettuce ensime_shared/spec/features
features := test/features

test: unit integration

$(activate):
virtualenv -p $(PYTHON) $(VENV)
Expand All @@ -20,16 +20,39 @@ $(deps): $(activate) $(requirements)
$(VENV)/bin/pip install --upgrade --requirement test-requirements.txt
touch $(deps)

unit: $(deps)
@echo "Running ensime-vim unit tests"
. $(activate) && py.test

integration: $(deps)
@echo "Running ensime-vim lettuce tests"
. $(activate) && lettuce $(features)

coverage: $(deps)
. $(activate) && \
coverage erase && \
coverage run --module pytest && \
coverage run --append $$(which lettuce) $(features) && \
coverage html && \
coverage report
@echo
@echo "Open htmlcov/index.html for an HTML report."

lint: $(deps)
. $(activate) && flake8 --statistics --count --show-source

format: $(deps)
. $(activate) && autopep8 -aaa --in-place -r $(REFORMAT)

clean:
@echo Cleaning build artifacts, including the virtualenv...
-rm -rf $(VENV)
@echo Cleaning build artifacts...
-find . -type f -name '*.py[c|o]' -delete
-find . -type d -name '__pycache__' -delete
. $(activate) && coverage erase
-$(RM) -r htmlcov

distclean: clean
@echo Cleaning the virtualenv...
-rm -rf $(VENV)

.PHONY: test lint format clean
.PHONY: test unit integration coverage lint format clean distclean
79 changes: 79 additions & 0 deletions ensime_shared/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# coding: utf-8

import collections
import os

import sexpdata

from ensime_shared.util import Util

BOOTSTRAPS_ROOT = os.path.join(os.environ['HOME'], '.config/ensime-vim/')
"""Default directory where ENSIME server bootstrap projects will be created."""

Expand Down Expand Up @@ -80,3 +85,77 @@
"append_line": 'call append({}, {!r})',
"redraw": "redraw!"
}


class ProjectConfig(collections.Mapping):
"""A dict-like immutable representation of an ENSIME project configuration.

Args:
filepath (str): Path of an ``.ensime`` file to parse.
"""

def __init__(self, filepath):
self._filepath = os.path.realpath(filepath)
self.__data = self.parse(filepath)

# Provide the Mapping protocol requirements

def __getitem__(self, key):
return self.__data[key]

def __iter__(self):
return iter(self.__data)

def __len__(self):
return len(self.__data)

def __repr__(self):
return "{name}({path!r})".format(
name=self.__class__.__name__,
path=self.filepath
)

@property
def filepath(self):
"""str: The canonical path of the represented config file."""
return self._filepath

@staticmethod
def parse(path):
"""Parse an ``.ensime`` config file from S-expressions.

Args:
path (str): Path of an ``.ensime`` file to parse.

Returns:
dict: Configuration values with string keys.
"""

def paired(iterable):
"""s -> (s0, s1), (s2, s3), (s4, s5), ..."""
cursor = iter(iterable)
return zip(cursor, cursor)

def unwrap_if_sexp_symbol(datum):
"""Convert Symbol(':key') to ':key' (Symbol isn't hashable for dict keys).
"""
return datum.value() if isinstance(datum, sexpdata.Symbol) else datum

def sexp2dict(sexps):
"""Transforms a nested list structure from sexpdata to dict."""
newdict = {}

# Turn flat list into associative pairs
for key, value in paired(sexps):
key = str(unwrap_if_sexp_symbol(key)).lstrip(':')

# Recursively transform nested lists
if isinstance(value, list) and value and isinstance(value[0], list):
newdict[key] = [sexp2dict(value[0])]
else:
newdict[key] = value

return newdict

conf = sexpdata.loads(Util.read_file(path))
return sexp2dict(conf)
45 changes: 2 additions & 43 deletions ensime_shared/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@

from string import Template

import sexpdata

from ensime_shared.config import BOOTSTRAPS_ROOT
from ensime_shared.config import BOOTSTRAPS_ROOT, ProjectConfig
from ensime_shared.errors import InvalidJavaPathError
from ensime_shared.util import catch, Util

Expand Down Expand Up @@ -65,7 +63,7 @@ def __init__(self, vim, config_path, server_v2, base_dir=BOOTSTRAPS_ROOT):
self.vim = vim
self.ensime_version = self.ENSIME_V2 if server_v2 else self.ENSIME_V1
self._config_path = os.path.abspath(config_path)
self.config = self.parse_config(self._config_path)
self.config = ProjectConfig(self._config_path)
self.base_dir = os.path.abspath(base_dir)
self.classpath_file = os.path.join(self.base_dir,
self.config['scala-version'],
Expand Down Expand Up @@ -229,45 +227,6 @@ def build_sbt(self):

return Template(src).substitute(replace)

@staticmethod
def parse_config(path):
"""Parse an .ensime project config file, from S-expressions to dict."""

def paired(iterable):
"""s -> (s0, s1), (s2, s3), (s4, s5), ..."""
cursor = iter(iterable)
return zip(cursor, cursor)

def unwrap_if_sexp_symbol(datum):
"""
Convert Symbol(':key') to ':key' (Symbol isn't hashable for dict keys).
"""
return datum.value() if isinstance(datum, sexpdata.Symbol) else datum

def sexp2dict(sexps):
"""
Transforms a nested list structure from sexpdata to dict.

NOTE: This probably isn't general for all S-expression shapes parsed by
sexpdata, focused only on .ensime thus far.
"""
newdict = {}

# Turn flat list into associative pairs
for key, value in paired(sexps):
key = str(unwrap_if_sexp_symbol(key)).lstrip(':')

# Recursively transform nested lists
if isinstance(value, list) and value and isinstance(value[0], list):
newdict[key] = [sexp2dict(value[0])]
else:
newdict[key] = value

return newdict

conf = sexpdata.loads(Util.read_file(path))
return sexp2dict(conf)

def reorder_classpath(self, classpath_file):
"""Reorder classpath and put monkeys-jar in the first place."""
success = False
Expand Down
Empty file.
28 changes: 0 additions & 28 deletions ensime_shared/spec/features/conf_parse.feature

This file was deleted.

53 changes: 0 additions & 53 deletions ensime_shared/spec/features/conf_parse_steps.py

This file was deleted.

21 changes: 19 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
# our case it's just a shared config file for linting and formatting tools
# because most of them support it automatically.

[pytest]
testpaths = test

[coverage:run]
branch = true
source = ensime_shared

[coverage:report]
exclude_lines =
# Need to re-enable the standard pragma
pragma: no cover

if __name__ == .__main__.:
def __str__
def __repr__
raise NotImplementedError

[flake8]
# Try to stick to 79, but sometimes being religious *hurts* readability.
max-line-length = 100
Expand All @@ -20,7 +37,7 @@ exclude =
syntax/,

# Currently very noisy, but we should strongly consider bringing this back.
ensime_shared/spec/
test/features/

[pep8]
max-line-length = 100
Expand All @@ -32,6 +49,6 @@ exclude =
plugin/,
plugin_integrations/,
syntax/,
ensime_shared/spec/
test/features/

# vim:set ft=dosini et sw=4 ts=4:
2 changes: 2 additions & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# === Test ===
coverage~=4.2
lettuce~=0.2.21
pytest~=2.9

# Optional speed-up for Lettuce, but Docker image build issue needs fixing, see:
# https://github.com/ensime/ensime-docker/pull/8
Expand Down
9 changes: 9 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import os
import sys

# pytest expects the project modules to be importable from whereever you run
# it, preferring that you do `pip install --editable .` -- we don't want to be
# a distributable Python package so this is easier than maintaining a useless
# setup.py.
parent = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent)
3 changes: 3 additions & 0 deletions test/resources/broken.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(
:name "testing"
:scala-version "2.11.8"
Loading