<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def write_puml(self, filename=''):
 """ Writes PUML from the system. If filename is given, stores result in the file. Otherwise returns result as a string. """

        def get_type(o):
            type = 'program'
            if isinstance(o, AbstractSensor):
                type = 'sensor'
            elif isinstance(o, AbstractActuator):
                type = 'actuator'
            return type

        if filename:
            s = open(filename, 'w')
        else:
            s = io.StringIO()
        s.write('@startuml\n')
        s.write('skinparam state {\n')
        for k, v in list(self.background_colors.items()):
            s.write('BackGroundColor<<%s>> %s\n' % (k, v))
        s.write('}\n')

        for o in self.system.objects:
            if isinstance(o, DefaultProgram) or o.hide_in_uml:
                continue

            if isinstance(o, ProgrammableSystemObject):
                s.write('state "%s" as %s <<%s>>\n' % (o, o, get_type(o)))

                s.write('%s: %s\n' % (o, o.class_name))
                if isinstance(o, AbstractActuator):
                    for p in reversed(o.program_stack):
                        s.write('%s: %s :: %s\n' % (o, p, o.program_status.get(p, '-')))
                elif hasattr(o, 'status'):
                    s.write('%s: Status: %s\n' % (o, o.status))
                if getattr(o, 'is_program', False):
                    s.write('%s: Priority: %s\n' % (o, o.priority))

                for t in o.actual_triggers:
                    if isinstance(t, DefaultProgram) or t.hide_in_uml:
                        continue
                    s.write('%s -[%s]-> %s\n' % (t, self.arrow_colors['trigger'], o))
                for t in o.actual_targets:
                    if t.hide_in_uml:
                        continue
                    if o.active:
                        color = 'active_target'
                    else:
                        color = 'inactive_target'
                    if getattr(t, 'program', None) == o:
                        color = 'controlled_target'

                    s.write('%s -[%s]-> %s\n' % (o, self.arrow_colors[color], t))
        s.write('@enduml\n')
        if filename:
            s.close()
        else:
            return s.getvalue()