Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No application singleton #262

Merged
merged 10 commits into from Feb 5, 2020
4 changes: 2 additions & 2 deletions examples/list_classes.py
Expand Up @@ -8,7 +8,7 @@
import sys

import gaphor.UML as UML
from gaphor.application import Application
from gaphor.application import Session

# Setup command line options.
usage = "usage: %prog [options] file.gaphor"
Expand All @@ -33,7 +33,7 @@
model = args[0]

# Create the Gaphor application object.
session = Application.new_session()
session = Session()

# Get services we need.
element_factory = session.get_service("element_factory")
Expand Down
27 changes: 12 additions & 15 deletions gaphor/UML/properties.py
Expand Up @@ -743,21 +743,18 @@ def _union(self, obj, exclude=None):
"""
Returns a union of all values as a set.
"""
if self.single:
return next(iter(self.subsets)).__get__(obj)
else:
u: Set[T] = set()
for s in self.subsets:
if s is exclude:
continue
tmp = s.__get__(obj)
if tmp:
try:
u.update(tmp)
except TypeError:
# [0..1] property
u.add(tmp)
return collectionlist(u)
u: Set[T] = set()
for s in self.subsets:
if s is exclude:
continue
tmp = s.__get__(obj)
if tmp:
try:
u.update(tmp)
except TypeError:
# [0..1] property
u.add(tmp)
return collectionlist(u)

def propagate(self, event):
"""
Expand Down
11 changes: 3 additions & 8 deletions gaphor/UML/tests/test_elementfactory.py
Expand Up @@ -2,7 +2,7 @@

import pytest

from gaphor.application import Application
from gaphor.application import Session
from gaphor.core import event_handler
from gaphor.services.eventmanager import EventManager
from gaphor.UML import Parameter
Expand Down Expand Up @@ -96,17 +96,12 @@ def clear_events():

@pytest.fixture
def element_factory():
session = Application.new_session(
services=["event_manager", "component_registry", "element_factory"]
)
event_manager = session.get_service("event_manager")
event_manager = EventManager()
event_manager.subscribe(handler)
clear_events()
factory = session.get_service("element_factory")
factory = ElementFactory(event_manager)
yield factory
del factory
clear_events()
session.shutdown()


def test_create_event(element_factory):
Expand Down
41 changes: 31 additions & 10 deletions gaphor/UML/tests/test_properties.py
@@ -1,6 +1,5 @@
import pytest

from gaphor.application import Application
from gaphor.core import event_handler
from gaphor.UML.element import Element
from gaphor.UML.event import AssociationUpdated
Expand Down Expand Up @@ -454,24 +453,44 @@ class A(Element):
assert d in a.u


@pytest.mark.skip
def test_derivedunion_notify():
def test_derivedunion_notify_for_single_derived_property():
class A(Element):
pass

class E(Element):
notified = False

def notify(self, name, pspec):
if name == "u":
def handle(self, event):
if event.property is E.u:
self.notified = True

E.a = association("a", A)
E.u = derivedunion(E, "u", A, 0, "*", E.a)

e = E()
assert e.notified is False
e.a = A()

assert e.notified is True


def test_derivedunion_notify_for_multiple_derived_properties():
class A(Element):
pass

class E(Element):
notified = False

def handle(self, event):
if event.property is E.u:
self.notified = True

E.a = association("a", A)
E.aa = association("aa", A)
E.u = derivedunion(E, "u", A, 0, "*", E.a, E.aa)

e = E()
e.a = A()

assert e.notified is True


Expand Down Expand Up @@ -546,7 +565,7 @@ def unlink(self):
def handler(event, events=events):
events.append(event)

Application.register_handler(handler)
# Application.register_handler(handler)
try:
a = A()
a.a1 = A()
Expand Down Expand Up @@ -636,7 +655,8 @@ def handler(event, events=events):
assert isinstance(events[13], DerivedDeleted), type(events[10])
assert events[14].property is A.b3
finally:
Application.unregister_handler(handler)
# Application.unregister_handler(handler)
pass


@pytest.mark.skip
Expand Down Expand Up @@ -692,7 +712,7 @@ class B(A):
def handler(event, events=events):
events.append(event)

Application.register_handler(handler)
# Application.register_handler(handler)
try:
a = A()
a.a = A()
Expand All @@ -709,4 +729,5 @@ def handler(event, events=events):
assert events[0].property is B.b, events[0].property
assert events[1].property is B.b.original, events[1].property
finally:
Application.unregister_handler(handler)
# Application.unregister_handler(handler)
amolenaar marked this conversation as resolved.
Show resolved Hide resolved
pass
35 changes: 21 additions & 14 deletions gaphor/application.py
Expand Up @@ -17,6 +17,7 @@

import importlib_metadata

from gaphor import transaction
from gaphor.abc import ActionProvider, Service
from gaphor.action import action
from gaphor.event import (
Expand All @@ -31,6 +32,13 @@
logger = logging.getLogger(__name__)


def distribution():
"""
The PkgResources distribution for Gaphor
"""
return importlib_metadata.distribution("gaphor")


class NotInitializedError(Exception):
pass

Expand All @@ -39,7 +47,7 @@ class ComponentLookupError(LookupError):
pass


class _Application(Service, ActionProvider):
class Application(Service, ActionProvider):
"""
The Gaphor application is started from the gaphor.ui module.

Expand All @@ -50,32 +58,28 @@ class _Application(Service, ActionProvider):
are registered in the "component_registry" service.
"""

def __init__(self):
def __init__(self, appservices=None):
self.active_session: Optional[Session] = None
self.sessions: Set[Session] = set()
self._services_by_name: Dict[str, Service] = {}

def init(self):
uninitialized_services = load_services("gaphor.appservices")
uninitialized_services = load_services("gaphor.appservices", appservices)
self._services_by_name = init_services(uninitialized_services, application=self)

transaction.subscribers.add(self._transaction_proxy)

def new_session(self, services=None):
"""
Initialize an application session.
"""
session = Session()
self.sessions.add(session)
self.active_session = session

return session

def has_sessions(self):
return bool(self.active_session)

distribution = property(
lambda s: importlib_metadata.distribution("gaphor"),
doc="The PkgResources distribution for Gaphor",
)

def shutdown_session(self, session):
assert session
session.shutdown()
Expand All @@ -89,6 +93,8 @@ def shutdown(self):

This is mainly for testing purposes.
"""
transaction.subscribers.discard(self._transaction_proxy)

while self.sessions:
self.shutdown_session(self.sessions.pop())

Expand All @@ -109,12 +115,17 @@ def quit(self):
if self.active_session == session:
logger.info("Window not closed, abort quit operation")
return
self.shutdown()

def all(self, base: Type[T]) -> Iterator[Tuple[str, T]]:
return (
(n, c) for n, c in self._services_by_name.items() if isinstance(c, base)
)

def _transaction_proxy(self, event):
if self.active_session:
self.active_session.event_manager.handle(event)


class Session:
"""
Expand Down Expand Up @@ -218,7 +229,3 @@ def init(name, cls):
init(name, cls)

return ready


# Make sure there is only one!
Application = _Application()
2 changes: 0 additions & 2 deletions gaphor/i18n.py
Expand Up @@ -10,8 +10,6 @@
import logging
import os

import importlib_metadata

log = logging.getLogger(__name__)

try:
Expand Down
8 changes: 5 additions & 3 deletions gaphor/services/session.py
@@ -1,15 +1,17 @@
from gaphor.abc import Service
from gaphor.application import Application


class Session(Service):
"""
Application service. Get the active session.
"""

def __init__(self, application):
self.application = application

def shutdown(self):
pass

def get_service(self, name):
assert Application.active_session
return Application.active_session.get_service(name)
assert self.application.active_session
return self.application.active_session.get_service(name)
5 changes: 2 additions & 3 deletions gaphor/storage/storage.py
Expand Up @@ -18,8 +18,7 @@

import gaphas

from gaphor import UML
from gaphor.application import Application
from gaphor import UML, application
from gaphor.i18n import gettext
from gaphor.storage import diagramitems, parser
from gaphor.UML.collection import collection
Expand Down Expand Up @@ -136,7 +135,7 @@ def save_canvasitem(name, value, reference=False):
None,
{
(NAMESPACE_MODEL, "version"): FILE_FORMAT_VERSION,
(NAMESPACE_MODEL, "gaphor-version"): Application.distribution.version,
(NAMESPACE_MODEL, "gaphor-version"): application.distribution().version,
},
)

Expand Down
12 changes: 4 additions & 8 deletions gaphor/storage/tests/test_storage.py
Expand Up @@ -5,9 +5,8 @@
import re
from io import StringIO

import importlib_metadata

from gaphor import UML
from gaphor.application import distribution
from gaphor.diagram.classes import AssociationItem, ClassItem, InterfaceItem
from gaphor.diagram.general import CommentItem
from gaphor.misc.xmlwriter import XMLWriter
Expand Down Expand Up @@ -152,8 +151,7 @@ def test_load_uml_metamodel(self):
Test if the meta model can be loaded.
"""

dist = importlib_metadata.distribution("gaphor")
path = dist.locate_file("gaphor/UML/uml2.gaphor")
path = distribution().locate_file("gaphor/UML/uml2.gaphor")

with open(path) as ifile:
storage.load(ifile, factory=self.element_factory)
Expand Down Expand Up @@ -250,8 +248,7 @@ def test_load_save(self):

"""Test loading and saving models"""

dist = importlib_metadata.distribution("gaphor")
path = dist.locate_file("test-diagrams/simple-items.gaphor")
path = distribution().locate_file("test-diagrams/simple-items.gaphor")

with open(path, "r") as ifile:
storage.load(ifile, factory=self.element_factory)
Expand Down Expand Up @@ -281,8 +278,7 @@ def test_loading_an_old_version(self):

"""Test loading and saving models"""

dist = importlib_metadata.distribution("gaphor")
path = dist.locate_file("test-diagrams/old-gaphor-version.gaphor")
path = distribution().locate_file("test-diagrams/old-gaphor-version.gaphor")

def load_old_model():
with open(path, "r") as ifile:
Expand Down
8 changes: 3 additions & 5 deletions gaphor/storage/tests/test_storage_message_item_upgrade.py
@@ -1,16 +1,15 @@
import importlib_metadata
import pytest

from gaphor import UML
from gaphor.application import Application
from gaphor.application import Session, distribution
from gaphor.storage import diagramitems
from gaphor.storage.parser import parse
from gaphor.storage.storage import load_elements


@pytest.fixture
def session():
session = Application.new_session(
session = Session(
services=["event_manager", "component_registry", "element_factory"]
)
yield session
Expand All @@ -25,8 +24,7 @@ def element_factory(session):
def test_message_item_upgrade(element_factory):
"""
"""
dist = importlib_metadata.distribution("gaphor")
path = dist.locate_file("test-diagrams/multiple-messages.gaphor")
path = distribution().locate_file("test-diagrams/multiple-messages.gaphor")

elements = parse(path)
load_elements(elements, element_factory)
Expand Down