Merge lp:~bladernr/opencompute/ocp-add-dcmitool-ipmitool-to-deps into lp:opencompute

Proposed by Jeff Lane 
Status: Superseded
Proposed branch: lp:~bladernr/opencompute/ocp-add-dcmitool-ipmitool-to-deps
Merge into: lp:opencompute
Diff against target: 609044 lines (+605588/-0) (has conflicts)
659 files modified
.bzr-builddeb/default.conf (+2/-0)
.bzrignore (+42/-0)
.checkbox-editor.cfg (+18/-0)
.gitignore (+26/-0)
.test-it.conf (+4/-0)
.travis.yml (+5/-0)
COPYING (+674/-0)
MANIFEST.in (+1/-0)
README (+5/-0)
README-PORT (+76/-0)
Vagrantfile (+56/-0)
apport/checkbox.py (+24/-0)
apport/source_checkbox.py (+20/-0)
backend (+66/-0)
bin/checkbox-hw-collection (+19/-0)
bin/checkbox-ocp-cli (+22/-0)
bin/checkbox-ocp-gtk (+22/-0)
bin/checkbox-ocp-qt (+22/-0)
bin/checkbox-ocp-urwid (+22/-0)
checkbox/application.py (+136/-0)
checkbox/arguments.py (+95/-0)
checkbox/attribute.py (+41/-0)
checkbox/component.py (+184/-0)
checkbox/contrib/REThread.py (+142/-0)
checkbox/contrib/bpickle.py (+188/-0)
checkbox/contrib/gdk.py (+46/-0)
checkbox/contrib/glock.py (+311/-0)
checkbox/contrib/persist.py (+552/-0)
checkbox/contrib/xrandr.py (+1064/-0)
checkbox/dbus/__init__.py (+89/-0)
checkbox/dbus/udisks2.py (+479/-0)
checkbox/dispatcher.py (+216/-0)
checkbox/heuristics/__init__.py (+56/-0)
checkbox/heuristics/tests/test_udisks2.py (+40/-0)
checkbox/heuristics/udev.py (+44/-0)
checkbox/heuristics/udisks2.py (+62/-0)
checkbox/job.py (+124/-0)
checkbox/lib/bit.py (+46/-0)
checkbox/lib/cache.py (+37/-0)
checkbox/lib/config.py (+170/-0)
checkbox/lib/conversion.py (+172/-0)
checkbox/lib/decorator.py (+28/-0)
checkbox/lib/dmi.py (+241/-0)
checkbox/lib/enum.py (+72/-0)
checkbox/lib/environ.py (+113/-0)
checkbox/lib/fifo.py (+107/-0)
checkbox/lib/input.py (+585/-0)
checkbox/lib/log.py (+77/-0)
checkbox/lib/path.py (+62/-0)
checkbox/lib/pci.py (+89/-0)
checkbox/lib/process.py (+140/-0)
checkbox/lib/redirect.py (+158/-0)
checkbox/lib/resolver.py (+145/-0)
checkbox/lib/safe.py (+100/-0)
checkbox/lib/script.py (+33/-0)
checkbox/lib/selector.py (+207/-0)
checkbox/lib/signal.py (+89/-0)
checkbox/lib/template.py (+143/-0)
checkbox/lib/template_i18n.py (+134/-0)
checkbox/lib/tests/test_resolver.py (+98/-0)
checkbox/lib/text.py (+100/-0)
checkbox/lib/transport.py (+306/-0)
checkbox/lib/tz.py (+55/-0)
checkbox/lib/update.py (+42/-0)
checkbox/lib/url.py (+41/-0)
checkbox/lib/usb.py (+59/-0)
checkbox/message.py (+334/-0)
checkbox/parsers/cpuinfo.py (+179/-0)
checkbox/parsers/cputable (+40/-0)
checkbox/parsers/cputable.py (+42/-0)
checkbox/parsers/deferred.py (+27/-0)
checkbox/parsers/description.py (+96/-0)
checkbox/parsers/device.py (+24/-0)
checkbox/parsers/dmidecode.py (+126/-0)
checkbox/parsers/efi.py (+52/-0)
checkbox/parsers/lshwjson.py (+23/-0)
checkbox/parsers/meminfo.py (+46/-0)
checkbox/parsers/modinfo.py (+89/-0)
checkbox/parsers/pactl.py (+543/-0)
checkbox/parsers/pyparsing.py (+3604/-0)
checkbox/parsers/submission.py (+568/-0)
checkbox/parsers/tests/fixtures/submission_attachment.xml (+6/-0)
checkbox/parsers/tests/fixtures/submission_info_cpuinfo.xml (+34/-0)
checkbox/parsers/tests/fixtures/submission_info_dmidecode.xml (+783/-0)
checkbox/parsers/tests/fixtures/submission_info_memory.xml (+48/-0)
checkbox/parsers/tests/fixtures/submission_info_udevadm.xml (+5365/-0)
checkbox/parsers/tests/fixtures/submission_lsbrelease.xml (+11/-0)
checkbox/parsers/tests/fixtures/submission_packages.xml (+13/-0)
checkbox/parsers/tests/fixtures/submission_processors.xml (+149/-0)
checkbox/parsers/tests/fixtures/submission_questions.xml (+17/-0)
checkbox/parsers/tests/fixtures/submission_udev.xml (+5364/-0)
checkbox/parsers/tests/fixtures/xinput_quantal.txt (+143/-0)
checkbox/parsers/tests/fixtures/xinput_toshiba.txt (+166/-0)
checkbox/parsers/tests/pactl_data/cards-desktop-precise-0.txt (+33/-0)
checkbox/parsers/tests/pactl_data/cards-desktop-precise-1.txt (+51/-0)
checkbox/parsers/tests/pactl_data/cards-desktop-precise-2.txt (+30/-0)
checkbox/parsers/tests/pactl_data/cards-desktop-precise.txt (+41/-0)
checkbox/parsers/tests/pactl_data/desktop-precise-radeon-hdmi-available.txt (+608/-0)
checkbox/parsers/tests/pactl_data/desktop-precise-radeon.txt (+608/-0)
checkbox/parsers/tests/pactl_data/desktop-precise-xps1340.txt (+466/-0)
checkbox/parsers/tests/pactl_data/desktop-precise.txt (+696/-0)
checkbox/parsers/tests/pactl_data/desktop-raring-t430s-dp-available.txt (+889/-0)
checkbox/parsers/tests/pactl_data/desktop-raring-t430s.txt (+889/-0)
checkbox/parsers/tests/pactl_data/modules-desktop-precise-0.txt (+8/-0)
checkbox/parsers/tests/pactl_data/modules-desktop-precise.txt (+213/-0)
checkbox/parsers/tests/pactl_data/samples-desktop-precise.txt (+60/-0)
checkbox/parsers/tests/pactl_data/sinks-desktop-precise-0.txt (+53/-0)
checkbox/parsers/tests/pactl_data/sinks-desktop-precise-1.txt (+56/-0)
checkbox/parsers/tests/pactl_data/sinks-desktop-precise.txt (+56/-0)
checkbox/parsers/tests/test_cputable.py (+74/-0)
checkbox/parsers/tests/test_description.py (+194/-0)
checkbox/parsers/tests/test_dmi.py (+80/-0)
checkbox/parsers/tests/test_dmidecode.py (+60/-0)
checkbox/parsers/tests/test_efi.py (+63/-0)
checkbox/parsers/tests/test_pactl.py (+561/-0)
checkbox/parsers/tests/test_submission.py (+183/-0)
checkbox/parsers/tests/test_udevadm.py (+80/-0)
checkbox/parsers/tests/test_xinput.py (+130/-0)
checkbox/parsers/udevadm.py (+538/-0)
checkbox/parsers/utils.py (+36/-0)
checkbox/parsers/xinput.py (+198/-0)
checkbox/plugin.py (+50/-0)
checkbox/properties.py (+209/-0)
checkbox/reactor.py (+118/-0)
checkbox/report.py (+170/-0)
checkbox/reports/launchpad_report.py (+287/-0)
checkbox/reports/xml_report.py (+193/-0)
checkbox/resource.py (+139/-0)
checkbox/scripts/audio_settings.py (+375/-0)
checkbox/scripts/gputest_benchmark.py (+106/-0)
checkbox/test_resource.py (+347/-0)
checkbox/tests/__init__.py (+49/-0)
checkbox/tests/test_job.py (+38/-0)
checkbox/tests/test_message_files.py (+123/-0)
checkbox/tests/test_report.py (+109/-0)
checkbox/tests/test_setup_files.py (+65/-0)
checkbox/udev.py (+93/-0)
checkbox/user_interface.py (+257/-0)
checkbox/variables.py (+303/-0)
checkbox_cli/cli_interface.py (+477/-0)
checkbox_gtk/gtk_interface.py (+647/-0)
checkbox_gtk/hyper_text_view.py (+114/-0)
checkbox_ocp/cli_interface.py (+477/-0)
checkbox_qt/qt_interface.py (+298/-0)
checkbox_urwid/urwid_interface.py (+1071/-0)
data/whitelists/default.whitelist (+143/-0)
data/whitelists/opencompute-ready-local.whitelist (+103/-0)
data/whitelists/opencompute-ready-remote.whitelist (+31/-0)
data/whitelists/server-cert.whitelist (+103/-0)
data/whitelists/sniff.whitelist (+35/-0)
debian/changelog (+45/-0)
debian/checkbox-hw-collection.install (+2/-0)
debian/checkbox-hw-collection.links (+1/-0)
debian/checkbox-hw-collection.postinst (+7/-0)
debian/checkbox-ocp-cli.install (+3/-0)
debian/checkbox-ocp-cli.links (+1/-0)
debian/checkbox-ocp-cli.postinst (+7/-0)
debian/checkbox-ocp-gtk.install (+4/-0)
debian/checkbox-ocp-gtk.links (+1/-0)
debian/checkbox-ocp-gtk.postinst (+7/-0)
debian/checkbox-ocp-qt.install (+6/-0)
debian/checkbox-ocp-qt.links (+1/-0)
debian/checkbox-ocp-qt.postinst (+7/-0)
debian/checkbox-ocp-urwid.install (+3/-0)
debian/checkbox-ocp-urwid.links (+1/-0)
debian/checkbox-ocp-urwid.postinst (+7/-0)
debian/checkbox.config (+27/-0)
debian/checkbox.dirs (+3/-0)
debian/checkbox.install (+19/-0)
debian/checkbox.links (+2/-0)
debian/checkbox.manpages (+1/-0)
debian/checkbox.postinst (+9/-0)
debian/checkbox.postrm (+26/-0)
debian/checkbox.templates (+83/-0)
debian/compat (+1/-0)
debian/control (+133/-0)
debian/copyright (+33/-0)
debian/po/POTFILES.in (+1/-0)
debian/po/ast.po (+210/-0)
debian/po/cs.po (+210/-0)
debian/po/de.po (+219/-0)
debian/po/en_AU.po (+210/-0)
debian/po/en_GB.po (+210/-0)
debian/po/es.po (+216/-0)
debian/po/fr.po (+212/-0)
debian/po/gl.po (+212/-0)
debian/po/he.po (+210/-0)
debian/po/hu.po (+210/-0)
debian/po/id.po (+218/-0)
debian/po/it.po (+216/-0)
debian/po/ja.po (+210/-0)
debian/po/nl.po (+218/-0)
debian/po/oc.po (+210/-0)
debian/po/pl.po (+210/-0)
debian/po/pt_BR.po (+214/-0)
debian/po/ro.po (+210/-0)
debian/po/ru.po (+210/-0)
debian/po/templates.pot (+210/-0)
debian/po/tr.po (+210/-0)
debian/po/uk.po (+210/-0)
debian/po/zh_CN.po (+210/-0)
debian/po/zh_TW.po (+210/-0)
debian/rules (+48/-0)
debian/source/format (+1/-0)
examples/checkbox-hw-collection.ini (+21/-0)
examples/checkbox-ocp-cli.ini (+17/-0)
examples/checkbox-ocp-gtk.ini (+16/-0)
examples/checkbox-ocp-qt.ini (+20/-0)
examples/checkbox-ocp-urwid.ini (+17/-0)
examples/checkbox.ini (+24/-0)
examples/network.cfg (+4/-0)
examples/org.freedesktop.policykit.checkbox.policy (+31/-0)
examples/virtualization.cfg (+4/-0)
gtk/checkbox-gtk.ui (+504/-0)
gtk/checkbox.svg (+2478/-0)
gtk/hyper_text_view.xml (+11/-0)
icons/scalable/apps/checkbox.svg (+336/-0)
install/config (+193/-0)
install/postinst (+72/-0)
jobs/benchmarks.txt.in (+297/-0)
jobs/cpu.txt.in (+50/-0)
jobs/dcmi_in_band.txt.in (+56/-0)
jobs/disk.txt.in (+120/-0)
jobs/info.txt.in (+226/-0)
jobs/ipmi.txt.in (+151/-0)
jobs/local.txt.in (+111/-0)
jobs/memory.txt.in (+28/-0)
jobs/miscellanea.txt.in (+148/-0)
jobs/networking.txt.in (+126/-0)
jobs/optical.txt.in (+165/-0)
jobs/power-management.txt.in (+232/-0)
jobs/resource.txt.in (+154/-0)
jobs/smoke.txt.in (+50/-0)
jobs/sniff.txt.in (+74/-0)
jobs/stress.txt.in (+239/-0)
jobs/usb.txt.in (+201/-0)
jobs/virtualization.txt.in (+40/-0)
lint (+146/-0)
man/checkbox.1 (+106/-0)
patches/0.1-ubuntu13 (+21/-0)
patches/0.1-ubuntu14 (+40/-0)
patches/0.1-ubuntu15 (+17/-0)
patches/0.1-ubuntu2 (+10/-0)
patches/0.1-ubuntu3 (+12/-0)
patches/0.1-ubuntu4 (+7/-0)
patches/0.1-ubuntu5 (+11/-0)
patches/0.1-ubuntu6 (+5/-0)
patches/0.1-ubuntu9 (+12/-0)
patches/0.14.2 (+18/-0)
patches/0.4 (+48/-0)
patches/0.5 (+30/-0)
patches/0.8 (+31/-0)
patches/0.9 (+39/-0)
plainbox/.coveragerc (+24/-0)
plainbox/.gitignore (+8/-0)
plainbox/.gitmodules (+3/-0)
plainbox/COPYING (+674/-0)
plainbox/MANIFEST.in (+10/-0)
plainbox/README.rst (+23/-0)
plainbox/Vagrantfile (+42/-0)
plainbox/contrib/com.canonical.certification.PlainBox1.service (+3/-0)
plainbox/contrib/dbus-mini-client.py (+430/-0)
plainbox/contrib/policykit_auth_admin_keep/org.freedesktop.policykit.pkexec.policy (+30/-0)
plainbox/contrib/policykit_yes/org.freedesktop.policykit.pkexec.policy (+29/-0)
plainbox/daily-package-testing/README (+18/-0)
plainbox/daily-package-testing/Vagrantfile (+48/-0)
plainbox/daily-package-testing/test-in-vagrant.sh (+59/-0)
plainbox/docs/appdev/index.rst (+39/-0)
plainbox/docs/author/checkbox-job-format.rst (+168/-0)
plainbox/docs/author/index.rst (+66/-0)
plainbox/docs/changelog.rst (+32/-0)
plainbox/docs/conf.py (+255/-0)
plainbox/docs/dev/architecture.rst (+40/-0)
plainbox/docs/dev/config.rst (+253/-0)
plainbox/docs/dev/index.rst (+19/-0)
plainbox/docs/dev/intro.rst (+244/-0)
plainbox/docs/dev/old.rst (+343/-0)
plainbox/docs/dev/reference.rst (+216/-0)
plainbox/docs/dev/resources.rst (+261/-0)
plainbox/docs/dev/trusted-launcher.rst (+209/-0)
plainbox/docs/glossary.rst (+104/-0)
plainbox/docs/index.rst (+65/-0)
plainbox/docs/usage.rst (+93/-0)
plainbox/mk-interesting-graphs.sh (+33/-0)
plainbox/mk-venv.sh (+195/-0)
plainbox/plainbox/__init__.py (+38/-0)
plainbox/plainbox/abc.py (+315/-0)
plainbox/plainbox/data/plainbox-ci-mailer.conf (+44/-0)
plainbox/plainbox/data/plainbox.conf (+26/-0)
plainbox/plainbox/data/plainbox.desktop (+10/-0)
plainbox/plainbox/data/report/checkbox.js (+16/-0)
plainbox/plainbox/data/report/hardware-1_0.rng (+533/-0)
plainbox/plainbox/data/report/styles.css (+258/-0)
plainbox/plainbox/impl/__init__.py (+123/-0)
plainbox/plainbox/impl/applogic.py (+287/-0)
plainbox/plainbox/impl/box.py (+100/-0)
plainbox/plainbox/impl/checkbox.py (+338/-0)
plainbox/plainbox/impl/color.py (+90/-0)
plainbox/plainbox/impl/commands/__init__.py (+372/-0)
plainbox/plainbox/impl/commands/analyze.py (+160/-0)
plainbox/plainbox/impl/commands/check_config.py (+92/-0)
plainbox/plainbox/impl/commands/checkbox.py (+103/-0)
plainbox/plainbox/impl/commands/crash.py (+74/-0)
plainbox/plainbox/impl/commands/dev.py (+64/-0)
plainbox/plainbox/impl/commands/logtest.py (+58/-0)
plainbox/plainbox/impl/commands/parse.py (+123/-0)
plainbox/plainbox/impl/commands/run.py (+415/-0)
plainbox/plainbox/impl/commands/script.py (+123/-0)
plainbox/plainbox/impl/commands/selftest.py (+86/-0)
plainbox/plainbox/impl/commands/service.py (+132/-0)
plainbox/plainbox/impl/commands/special.py (+159/-0)
plainbox/plainbox/impl/commands/sru.py (+291/-0)
plainbox/plainbox/impl/commands/test_dev.py (+78/-0)
plainbox/plainbox/impl/commands/test_parse.py (+133/-0)
plainbox/plainbox/impl/commands/test_run.py (+168/-0)
plainbox/plainbox/impl/commands/test_script.py (+158/-0)
plainbox/plainbox/impl/commands/test_sru.py (+76/-0)
plainbox/plainbox/impl/config.py (+558/-0)
plainbox/plainbox/impl/dbus/__init__.py (+47/-0)
plainbox/plainbox/impl/dbus/decorators.py (+351/-0)
plainbox/plainbox/impl/dbus/service.py (+662/-0)
plainbox/plainbox/impl/depmgr.py (+334/-0)
plainbox/plainbox/impl/exporter/__init__.py (+300/-0)
plainbox/plainbox/impl/exporter/html.py (+145/-0)
plainbox/plainbox/impl/exporter/json.py (+55/-0)
plainbox/plainbox/impl/exporter/rfc822.py (+48/-0)
plainbox/plainbox/impl/exporter/test_html.py (+142/-0)
plainbox/plainbox/impl/exporter/test_init.py (+222/-0)
plainbox/plainbox/impl/exporter/test_json.py (+63/-0)
plainbox/plainbox/impl/exporter/test_rfc822.py (+47/-0)
plainbox/plainbox/impl/exporter/test_text.py (+43/-0)
plainbox/plainbox/impl/exporter/test_xml.py (+96/-0)
plainbox/plainbox/impl/exporter/text.py (+41/-0)
plainbox/plainbox/impl/exporter/xlsx.py (+570/-0)
plainbox/plainbox/impl/exporter/xml.py (+387/-0)
plainbox/plainbox/impl/highlevel.py (+117/-0)
plainbox/plainbox/impl/integration_tests.py (+188/-0)
plainbox/plainbox/impl/job.py (+260/-0)
plainbox/plainbox/impl/logging.py (+420/-0)
plainbox/plainbox/impl/mock_job.py (+36/-0)
plainbox/plainbox/impl/parsers.py (+157/-0)
plainbox/plainbox/impl/plugins.py (+207/-0)
plainbox/plainbox/impl/provider.py (+165/-0)
plainbox/plainbox/impl/providers/__init__.py (+58/-0)
plainbox/plainbox/impl/providers/checkbox.py (+117/-0)
plainbox/plainbox/impl/providers/special.py (+69/-0)
plainbox/plainbox/impl/providers/stubbox/__init__.py (+43/-0)
plainbox/plainbox/impl/providers/stubbox/data/whitelists/stub.whitelist (+12/-0)
plainbox/plainbox/impl/providers/stubbox/data/whitelists/stub1.whitelist (+7/-0)
plainbox/plainbox/impl/providers/stubbox/data/whitelists/stub2.whitelist (+7/-0)
plainbox/plainbox/impl/providers/stubbox/jobs/local.txt.in (+5/-0)
plainbox/plainbox/impl/providers/stubbox/jobs/multilevel.txt.in (+20/-0)
plainbox/plainbox/impl/providers/stubbox/jobs/stub.txt.in (+80/-0)
plainbox/plainbox/impl/providers/stubbox/scripts/stub_package_list (+3/-0)
plainbox/plainbox/impl/providers/test_checkbox.py (+40/-0)
plainbox/plainbox/impl/providers/test_special.py (+104/-0)
plainbox/plainbox/impl/providers/v1.py (+229/-0)
plainbox/plainbox/impl/resource.py (+487/-0)
plainbox/plainbox/impl/result.py (+271/-0)
plainbox/plainbox/impl/rfc822.py (+287/-0)
plainbox/plainbox/impl/runner.py (+445/-0)
plainbox/plainbox/impl/secure/__init__.py (+27/-0)
plainbox/plainbox/impl/secure/checkbox_trusted_launcher.py (+407/-0)
plainbox/plainbox/impl/secure/test_checkbox_trusted_launcher.py (+280/-0)
plainbox/plainbox/impl/service.py (+1058/-0)
plainbox/plainbox/impl/session.py (+884/-0)
plainbox/plainbox/impl/session/__init__.py (+95/-0)
plainbox/plainbox/impl/session/jobs.py (+290/-0)
plainbox/plainbox/impl/session/legacy.py (+271/-0)
plainbox/plainbox/impl/session/manager.py (+241/-0)
plainbox/plainbox/impl/session/resume.py (+477/-0)
plainbox/plainbox/impl/session/state.py (+651/-0)
plainbox/plainbox/impl/session/storage.py (+608/-0)
plainbox/plainbox/impl/session/suspend.py (+321/-0)
plainbox/plainbox/impl/session/test_jobs.py (+218/-0)
plainbox/plainbox/impl/session/test_legacy.py (+65/-0)
plainbox/plainbox/impl/session/test_resume.py (+1290/-0)
plainbox/plainbox/impl/session/test_state.py (+556/-0)
plainbox/plainbox/impl/session/test_storage.py (+161/-0)
plainbox/plainbox/impl/session/test_suspend.py (+502/-0)
plainbox/plainbox/impl/signal.py (+128/-0)
plainbox/plainbox/impl/test_applogic.py (+170/-0)
plainbox/plainbox/impl/test_box.py (+299/-0)
plainbox/plainbox/impl/test_checkbox.py (+83/-0)
plainbox/plainbox/impl/test_color.py (+40/-0)
plainbox/plainbox/impl/test_config.py (+164/-0)
plainbox/plainbox/impl/test_depmgr.py (+313/-0)
plainbox/plainbox/impl/test_job.py (+349/-0)
plainbox/plainbox/impl/test_plugins.py (+133/-0)
plainbox/plainbox/impl/test_resource.py (+342/-0)
plainbox/plainbox/impl/test_result.py (+128/-0)
plainbox/plainbox/impl/test_rfc822.py (+329/-0)
plainbox/plainbox/impl/test_runner.py (+179/-0)
plainbox/plainbox/impl/test_session.py (+845/-0)
plainbox/plainbox/impl/test_signal.py (+46/-0)
plainbox/plainbox/impl/test_testing_utils.py (+54/-0)
plainbox/plainbox/impl/testing_utils.py (+121/-0)
plainbox/plainbox/impl/transport/__init__.py (+95/-0)
plainbox/plainbox/impl/transport/certification.py (+132/-0)
plainbox/plainbox/impl/transport/test_certification.py (+183/-0)
plainbox/plainbox/impl/transport/test_init.py (+68/-0)
plainbox/plainbox/public.py (+50/-0)
plainbox/plainbox/test-data/html-exporter/example-data.html (+10931/-0)
plainbox/plainbox/test-data/html-exporter/html-inliner.html (+19/-0)
plainbox/plainbox/test-data/integration-tests/cpu/scaling_test.json (+10/-0)
plainbox/plainbox/test-data/integration-tests/smoke/true.json (+11/-0)
plainbox/plainbox/test-data/xml-exporter/example-data.json (+19037/-0)
plainbox/plainbox/test-data/xml-exporter/example-data.xml (+15749/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_binary_attachment.json (+7/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_binary_attachment.xml (+21/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_comments.json (+13/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_comments.xml (+30/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_hardware_info.json (+10/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_hardware_info.xml (+22/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_io_log.json (+14/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_io_log.xml (+30/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_text_attachment.json (+7/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_text_attachment.xml (+21/-0)
plainbox/plainbox/test_abc.py (+36/-0)
plainbox/plainbox/test_public.py (+36/-0)
plainbox/plainbox/testing_utils/__init__.py (+72/-0)
plainbox/plainbox/testing_utils/cwd.py (+46/-0)
plainbox/plainbox/testing_utils/io.py (+108/-0)
plainbox/plainbox/testing_utils/resource.py (+104/-0)
plainbox/plainbox/testing_utils/test_cwd.py (+43/-0)
plainbox/plainbox/testing_utils/test_io.py (+78/-0)
plainbox/plainbox/testing_utils/test_testcases.py (+147/-0)
plainbox/plainbox/testing_utils/testcases.py (+297/-0)
plainbox/plainbox/tests.py (+56/-0)
plainbox/plainbox/vendor/__init__.py (+28/-0)
plainbox/plainbox/vendor/extcmd/__init__.py (+759/-0)
plainbox/plainbox/vendor/extcmd/test.py (+100/-0)
plainbox/plainbox/vendor/funcsigs/LICENSE (+13/-0)
plainbox/plainbox/vendor/funcsigs/__init__.py (+810/-0)
plainbox/plainbox/vendor/funcsigs/version.py (+1/-0)
plainbox/requirements/deb-core.txt (+2/-0)
plainbox/requirements/deb-dbus.txt (+1/-0)
plainbox/requirements/deb-docs.txt (+1/-0)
plainbox/requirements/pip-core.txt (+2/-0)
plainbox/requirements/pip-dbus.txt (+1/-0)
plainbox/requirements/pip-docs.txt (+1/-0)
plainbox/requirements/pip-optional.txt (+1/-0)
plainbox/requirements/pip-tests.txt (+1/-0)
plainbox/requirements/rtfd.txt (+5/-0)
plainbox/setup.cfg (+5/-0)
plainbox/setup.py (+72/-0)
plainbox/test-in-vagrant.sh (+82/-0)
plainbox/test-with-coverage.sh (+8/-0)
plugins/attachment_info.py (+39/-0)
plugins/backend_info.py (+192/-0)
plugins/begin_prompt.py (+33/-0)
plugins/client_info.py (+43/-0)
plugins/datetime_info.py (+36/-0)
plugins/delay_prompt.py (+37/-0)
plugins/environment_info.py (+62/-0)
plugins/error_prompt.py (+35/-0)
plugins/final_prompt.py (+42/-0)
plugins/gather_prompt.py (+39/-0)
plugins/hexr_prompt.py (+32/-0)
plugins/hexr_transport.py (+171/-0)
plugins/intro_prompt.py (+67/-0)
plugins/jobs_info.py (+332/-0)
plugins/jobs_prompt.py (+193/-0)
plugins/launchpad_exchange.py (+183/-0)
plugins/launchpad_prompt.py (+113/-0)
plugins/launchpad_report.py (+228/-0)
plugins/local_suite.py (+47/-0)
plugins/lock_prompt.py (+86/-0)
plugins/manual_test.py (+52/-0)
plugins/message_info.py (+142/-0)
plugins/metric_test.py (+41/-0)
plugins/persist_info.py (+70/-0)
plugins/proxy_info.py (+84/-0)
plugins/recover_prompt.py (+77/-0)
plugins/remote_suite.py (+55/-0)
plugins/report_prompt.py (+46/-0)
plugins/resource_info.py (+78/-0)
plugins/scripts_info.py (+41/-0)
plugins/server_info.py (+42/-0)
plugins/shell_test.py (+63/-0)
plugins/sleep_info.py (+42/-0)
plugins/submission_info.py (+64/-0)
plugins/subunit_report.py (+79/-0)
plugins/suites_prompt.py (+164/-0)
plugins/system_info.py (+89/-0)
plugins/tests_prompt.py (+33/-0)
plugins/user_interface.py (+116/-0)
plugins/warning_prompt.py (+35/-0)
po/Makevars (+42/-0)
po/POTFILES.in (+55/-0)
po/ace.po (+3782/-0)
po/af.po (+3776/-0)
po/am.po (+3803/-0)
po/ar.po (+4279/-0)
po/ast.po (+7372/-0)
po/az.po (+3767/-0)
po/be.po (+5465/-0)
po/bg.po (+4363/-0)
po/bn.po (+6311/-0)
po/bo.po (+3927/-0)
po/br.po (+4019/-0)
po/bs.po (+7518/-0)
po/ca.po (+4997/-0)
po/ca@valencia.po (+4766/-0)
po/checkbox.pot (+3754/-0)
po/ckb.po (+3839/-0)
po/cs.po (+7464/-0)
po/cy.po (+3770/-0)
po/da.po (+5678/-0)
po/de.po (+8280/-0)
po/dv.po (+3761/-0)
po/el.po (+7134/-0)
po/en_AU.po (+7417/-0)
po/en_CA.po (+4213/-0)
po/en_GB.po (+7648/-0)
po/eo.po (+4898/-0)
po/es.po (+7897/-0)
po/et.po (+3866/-0)
po/eu.po (+4011/-0)
po/fa.po (+3761/-0)
po/fi.po (+6510/-0)
po/fr.po (+7945/-0)
po/ga.po (+3761/-0)
po/gd.po (+5282/-0)
po/gl.po (+7547/-0)
po/he.po (+4654/-0)
po/hi.po (+4322/-0)
po/hr.po (+4089/-0)
po/hu.po (+7748/-0)
po/hy.po (+3761/-0)
po/id.po (+4114/-0)
po/is.po (+3877/-0)
po/it.po (+7894/-0)
po/ja.po (+5348/-0)
po/jbo.po (+3761/-0)
po/ka.po (+3764/-0)
po/kk.po (+3898/-0)
po/km.po (+5573/-0)
po/kn.po (+3788/-0)
po/ko.po (+4596/-0)
po/ku.po (+3788/-0)
po/ky.po (+3761/-0)
po/lt.po (+4083/-0)
po/lv.po (+5119/-0)
po/mk.po (+3798/-0)
po/ml.po (+3773/-0)
po/mr.po (+3779/-0)
po/ms.po (+7625/-0)
po/my.po (+3858/-0)
po/nb.po (+4107/-0)
po/nds.po (+3761/-0)
po/ne.po (+3846/-0)
po/nl.po (+7802/-0)
po/nn.po (+3789/-0)
po/oc.po (+5036/-0)
po/pl.po (+5151/-0)
po/ps.po (+3761/-0)
po/pt.po (+5273/-0)
po/pt_BR.po (+7815/-0)
po/ro.po (+6314/-0)
po/ru.po (+7908/-0)
po/sd.po (+3761/-0)
po/shn.po (+3761/-0)
po/si.po (+3788/-0)
po/sk.po (+4575/-0)
po/sl.po (+7529/-0)
po/sq.po (+5296/-0)
po/sr.po (+5410/-0)
po/sv.po (+5478/-0)
po/ta.po (+3781/-0)
po/te.po (+3770/-0)
po/th.po (+4064/-0)
po/tr.po (+6932/-0)
po/ug.po (+7341/-0)
po/uk.po (+4922/-0)
po/ur.po (+3761/-0)
po/uz.po (+3761/-0)
po/vi.po (+4313/-0)
po/zh_CN.po (+6090/-0)
po/zh_HK.po (+4266/-0)
po/zh_TW.po (+4566/-0)
qt/checkbox-qt-head.svg (+163/-0)
qt/checkbox-qt.desktop.in (+11/-0)
qt/com.canonical.QtCheckbox.service (+3/-0)
qt/frontend/checkboxtr.cpp (+49/-0)
qt/frontend/checkboxtr.h (+47/-0)
qt/frontend/frontend.pro (+29/-0)
qt/frontend/main.cpp (+18/-0)
qt/frontend/qtfront.cpp (+627/-0)
qt/frontend/qtfront.h (+113/-0)
qt/frontend/qtfront.ui (+1198/-0)
qt/frontend/resources.qrc (+5/-0)
qt/frontend/step.cpp (+62/-0)
qt/frontend/step.h (+13/-0)
qt/frontend/treemodel.cpp (+110/-0)
qt/frontend/treemodel.h (+22/-0)
report/checkbox.js (+16/-0)
report/checkbox.xsl (+188/-0)
report/hardware-1_0.rng (+533/-0)
report/styles.css (+258/-0)
requirements/deb-core.txt (+1/-0)
requirements/deb-jobs.txt (+1/-0)
requirements/pip-core.txt (+1/-0)
requirements/pip-tests.txt (+1/-0)
run (+33/-0)
scripts/ansi_parser (+160/-0)
scripts/block_device_resource (+71/-0)
scripts/c_and_I_Ready.py (+151/-0)
scripts/cdimage_resource (+100/-0)
scripts/clocktest.c (+124/-0)
scripts/cpu_offlining (+46/-0)
scripts/cpu_topology (+103/-0)
scripts/cpuinfo_resource (+56/-0)
scripts/disk_read_performance_test (+74/-0)
scripts/disk_smart (+247/-0)
scripts/disk_stats_test (+68/-0)
scripts/disk_stress (+51/-0)
scripts/dmi_resource (+55/-0)
scripts/dns_server_test (+49/-0)
scripts/dpkg_resource (+56/-0)
scripts/efi_resource (+54/-0)
scripts/filter_packages (+183/-0)
scripts/filter_templates (+134/-0)
scripts/frequency_governors_test (+648/-0)
scripts/fwts_test (+371/-0)
scripts/gconf_resource (+95/-0)
scripts/internet_test (+273/-0)
scripts/ipmi_test (+39/-0)
scripts/lsb_resource (+56/-0)
scripts/lsmod_info (+40/-0)
scripts/meminfo_resource (+46/-0)
scripts/memory_compare (+91/-0)
scripts/memory_test (+236/-0)
scripts/module_resource (+73/-0)
scripts/network (+388/-0)
scripts/network_check (+72/-0)
scripts/network_device_info (+263/-0)
scripts/network_info (+67/-0)
scripts/network_ntp_test (+212/-0)
scripts/ocp3_initialize.sh (+32/-0)
scripts/optical_detect (+29/-0)
scripts/optical_read_test (+134/-0)
scripts/optical_write_test (+143/-0)
scripts/removable_storage_test (+594/-0)
scripts/removable_storage_watcher (+890/-0)
scripts/run_templates (+144/-0)
scripts/spindown (+128/-0)
scripts/storage_test (+80/-0)
scripts/threaded_memtest.c (+421/-0)
scripts/udev_resource (+63/-0)
scripts/udisks2_monitor (+154/-0)
scripts/uname_resource (+39/-0)
scripts/virt_check (+43/-0)
scripts/virtualization (+284/-0)
setup.cfg (+27/-0)
setup.py (+282/-0)
tarmac-verify (+19/-0)
test (+217/-0)
test-in-vagrant.sh (+122/-0)
Conflict adding file .gitignore.  Moved existing file to .gitignore.moved.
Conflict adding file scripts.  Moved existing file to scripts.moved.
To merge this branch: bzr merge lp:~bladernr/opencompute/ocp-add-dcmitool-ipmitool-to-deps
Reviewer Review Type Date Requested Status
Open Compute Administrators Pending
Review via email: mp+188684@code.launchpad.net

This proposal has been superseded by a proposal from 2013-10-01.

Commit message

This adds dmcitool and ipmitool to recommends to make sure they get installed as well.

Description of the change

Don't know what happened to my other branch, but for some reason, LP just dumped all the code but kept the branch open :/ Maybe it was something screwed up on my end. IN any case, this is a re-try.

This adds dmcitool and ipmitool to recommends to make sure they get installed as well.

To post a comment you must log in.

Unmerged revisions

2164. By Jeff Lane 

Another test run highlighted that neither dmcitool nor ipmitool are installed either. I don't think it'll hurt to install them side by side, so this adds them to the recommends as well

2163. By Jeff Marcom

Trivial fix to debian/control for build-system parser

Signed-off-by: Jeff Marcom <email address hidden>

2162. By Jeff Marcom

Clean up job description for ipmi tests

Signed-off-by: Jeff Marcom <email address hidden>

2161. By Jeff Lane 

Promotes several test packages from suggests to recommends.

2160. By Jeff Lane 

This does two things:
1: Enables the storage_devices tests using bonnie++ after determining that Bonnie is easily available via both apt and yum (for CentOS).
2: Adds a new io_stress test for disks that uses the disk I/O capabilities of googles stressapptest

2159. By Jeff Marcom

Merge adds new ipmi inband tests and fixes the job file name and test names to be more appropriate. Also separates them from miscellanea as IPMI tests have grown to a full suite now.

2158. By Jeff Marcom

Updated idle/check expression

Signed-off-by: Jeff Marcom <email address hidden>

2157. By Jeff Marcom

Fix symlink check

Signed-off-by: Jeff Marcom <email address hidden>

2156. By Jeff Marcom

Quick patch to miscellanea/idle_check to include job description so unit test will pass

Signed-off-by: Jeff Marcom <email address hidden>

2155. By Jeff Marcom

Added a simple check to bypass the symlink creation in an Ubuntu based release.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.bzr-builddeb'
2=== added file '.bzr-builddeb/default.conf'
3--- .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000
4+++ .bzr-builddeb/default.conf 2013-10-01 18:38:21 +0000
5@@ -0,0 +1,2 @@
6+[BUILDDEB]
7+native = True
8
9=== added file '.bzrignore'
10--- .bzrignore 1970-01-01 00:00:00 +0000
11+++ .bzrignore 2013-10-01 18:38:21 +0000
12@@ -0,0 +1,42 @@
13+__pycache__
14+build
15+checkbox.log
16+checkbox.xsl
17+debian/*-cli.postrm
18+debian/*-gtk.postrm
19+debian/*-qt.postrm
20+debian/*-urwid.postrm
21+debian/*.debhelper
22+debian/*.log
23+debian/*.substvars
24+debian/checkbox
25+debian/checkbox-cli
26+debian/checkbox-gtk
27+debian/checkbox-qt
28+debian/checkbox-urwid
29+debian/files
30+debian/stamp-patched
31+debian/tmp
32+gtk/checkbox-gtk.glade.bak
33+gtk/checkbox-gtk.gladep
34+gtk/checkbox-gtk.gladep.bak
35+lock
36+plugins.bpickle
37+plugins.bpickle.old
38+python-build-stamp-2.5
39+qt/frontend/Makefile
40+qt/frontend/checkbox-qt-service
41+qt/frontend/moc_qtfront.cpp
42+qt/frontend/qrc_resources.cpp
43+qt/frontend/ui_qtfront.h
44+scripts/clocktest
45+scripts/threaded_memtest
46+store
47+submission
48+submission.xml
49+submission.xml
50+subunit.log
51+system
52+vagrant-logs/
53+.vagrant
54+plainbox/plainbox.egg-info
55
56=== added file '.checkbox-editor.cfg'
57--- .checkbox-editor.cfg 1970-01-01 00:00:00 +0000
58+++ .checkbox-editor.cfg 2013-10-01 18:38:21 +0000
59@@ -0,0 +1,18 @@
60+[environment]
61+checkbox_share = .
62+
63+[version control]
64+push_branch = lp:checkbox
65+pull_branch = lp:checkbox
66+push_on_close = False
67+commit_on_save = False
68+pull_on_open = False
69+exclude = .checkbox-editor.cfg
70+
71+[general]
72+checkbox_data_cleanup = True
73+replace_tabs_with_spaces = True
74+number_of_spaces = 4
75+checkbox_binary = ./bin/checkbox-gtk
76+whitelist_resources = True
77+
78
79=== added file '.gitignore'
80--- .gitignore 1970-01-01 00:00:00 +0000
81+++ .gitignore 2013-10-01 18:38:21 +0000
82@@ -0,0 +1,26 @@
83+__pycache__
84+# Ignore logs created by test-in-vagrant.sh
85+vagrant-logs/
86+# Ignore vagrant state file
87+.vagrant
88+# The list below is riddled with junk created by running the test suite of checkbox
89+# It should be really cleaned up as tests should _not_ do that but right now it's more
90+# annoying than dangerous so I'll get rid of the noise first
91+*.o
92+active_output
93+checkbox.log
94+checkbox.xsl
95+plugins.bpickle
96+plugins.bpickle.old
97+qt/frontend/Makefile
98+qt/frontend/checkbox-qt-service
99+qt/frontend/moc_qtfront.cpp
100+qt/frontend/qrc_resources.cpp
101+qt/frontend/ui_qtfront.cpp
102+qt/frontend/ui_qtfront.h
103+store/
104+submission.xml
105+subunit.log
106+build
107+dist
108+*.egg-info
109
110=== renamed file '.gitignore' => '.gitignore.moved'
111=== added file '.test-it.conf'
112--- .test-it.conf 1970-01-01 00:00:00 +0000
113+++ .test-it.conf 2013-10-01 18:38:21 +0000
114@@ -0,0 +1,4 @@
115+# Configuration data for test-it
116+TI_RELEASES="lucid precise quantal raring"
117+TI_CMD="python3 test"
118+TI_DEPS="python3-lxml python3-setuptools"
119
120=== added file '.travis.yml'
121--- .travis.yml 1970-01-01 00:00:00 +0000
122+++ .travis.yml 2013-10-01 18:38:21 +0000
123@@ -0,0 +1,5 @@
124+language: python
125+python:
126+ - "3.2"
127+ - "3.3"
128+script: cd plainbox && python3 setup.py test
129
130=== added file 'COPYING'
131--- COPYING 1970-01-01 00:00:00 +0000
132+++ COPYING 2013-10-01 18:38:21 +0000
133@@ -0,0 +1,674 @@
134+ GNU GENERAL PUBLIC LICENSE
135+ Version 3, 29 June 2007
136+
137+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
138+ Everyone is permitted to copy and distribute verbatim copies
139+ of this license document, but changing it is not allowed.
140+
141+ Preamble
142+
143+ The GNU General Public License is a free, copyleft license for
144+software and other kinds of works.
145+
146+ The licenses for most software and other practical works are designed
147+to take away your freedom to share and change the works. By contrast,
148+the GNU General Public License is intended to guarantee your freedom to
149+share and change all versions of a program--to make sure it remains free
150+software for all its users. We, the Free Software Foundation, use the
151+GNU General Public License for most of our software; it applies also to
152+any other work released this way by its authors. You can apply it to
153+your programs, too.
154+
155+ When we speak of free software, we are referring to freedom, not
156+price. Our General Public Licenses are designed to make sure that you
157+have the freedom to distribute copies of free software (and charge for
158+them if you wish), that you receive source code or can get it if you
159+want it, that you can change the software or use pieces of it in new
160+free programs, and that you know you can do these things.
161+
162+ To protect your rights, we need to prevent others from denying you
163+these rights or asking you to surrender the rights. Therefore, you have
164+certain responsibilities if you distribute copies of the software, or if
165+you modify it: responsibilities to respect the freedom of others.
166+
167+ For example, if you distribute copies of such a program, whether
168+gratis or for a fee, you must pass on to the recipients the same
169+freedoms that you received. You must make sure that they, too, receive
170+or can get the source code. And you must show them these terms so they
171+know their rights.
172+
173+ Developers that use the GNU GPL protect your rights with two steps:
174+(1) assert copyright on the software, and (2) offer you this License
175+giving you legal permission to copy, distribute and/or modify it.
176+
177+ For the developers' and authors' protection, the GPL clearly explains
178+that there is no warranty for this free software. For both users' and
179+authors' sake, the GPL requires that modified versions be marked as
180+changed, so that their problems will not be attributed erroneously to
181+authors of previous versions.
182+
183+ Some devices are designed to deny users access to install or run
184+modified versions of the software inside them, although the manufacturer
185+can do so. This is fundamentally incompatible with the aim of
186+protecting users' freedom to change the software. The systematic
187+pattern of such abuse occurs in the area of products for individuals to
188+use, which is precisely where it is most unacceptable. Therefore, we
189+have designed this version of the GPL to prohibit the practice for those
190+products. If such problems arise substantially in other domains, we
191+stand ready to extend this provision to those domains in future versions
192+of the GPL, as needed to protect the freedom of users.
193+
194+ Finally, every program is threatened constantly by software patents.
195+States should not allow patents to restrict development and use of
196+software on general-purpose computers, but in those that do, we wish to
197+avoid the special danger that patents applied to a free program could
198+make it effectively proprietary. To prevent this, the GPL assures that
199+patents cannot be used to render the program non-free.
200+
201+ The precise terms and conditions for copying, distribution and
202+modification follow.
203+
204+ TERMS AND CONDITIONS
205+
206+ 0. Definitions.
207+
208+ "This License" refers to version 3 of the GNU General Public License.
209+
210+ "Copyright" also means copyright-like laws that apply to other kinds of
211+works, such as semiconductor masks.
212+
213+ "The Program" refers to any copyrightable work licensed under this
214+License. Each licensee is addressed as "you". "Licensees" and
215+"recipients" may be individuals or organizations.
216+
217+ To "modify" a work means to copy from or adapt all or part of the work
218+in a fashion requiring copyright permission, other than the making of an
219+exact copy. The resulting work is called a "modified version" of the
220+earlier work or a work "based on" the earlier work.
221+
222+ A "covered work" means either the unmodified Program or a work based
223+on the Program.
224+
225+ To "propagate" a work means to do anything with it that, without
226+permission, would make you directly or secondarily liable for
227+infringement under applicable copyright law, except executing it on a
228+computer or modifying a private copy. Propagation includes copying,
229+distribution (with or without modification), making available to the
230+public, and in some countries other activities as well.
231+
232+ To "convey" a work means any kind of propagation that enables other
233+parties to make or receive copies. Mere interaction with a user through
234+a computer network, with no transfer of a copy, is not conveying.
235+
236+ An interactive user interface displays "Appropriate Legal Notices"
237+to the extent that it includes a convenient and prominently visible
238+feature that (1) displays an appropriate copyright notice, and (2)
239+tells the user that there is no warranty for the work (except to the
240+extent that warranties are provided), that licensees may convey the
241+work under this License, and how to view a copy of this License. If
242+the interface presents a list of user commands or options, such as a
243+menu, a prominent item in the list meets this criterion.
244+
245+ 1. Source Code.
246+
247+ The "source code" for a work means the preferred form of the work
248+for making modifications to it. "Object code" means any non-source
249+form of a work.
250+
251+ A "Standard Interface" means an interface that either is an official
252+standard defined by a recognized standards body, or, in the case of
253+interfaces specified for a particular programming language, one that
254+is widely used among developers working in that language.
255+
256+ The "System Libraries" of an executable work include anything, other
257+than the work as a whole, that (a) is included in the normal form of
258+packaging a Major Component, but which is not part of that Major
259+Component, and (b) serves only to enable use of the work with that
260+Major Component, or to implement a Standard Interface for which an
261+implementation is available to the public in source code form. A
262+"Major Component", in this context, means a major essential component
263+(kernel, window system, and so on) of the specific operating system
264+(if any) on which the executable work runs, or a compiler used to
265+produce the work, or an object code interpreter used to run it.
266+
267+ The "Corresponding Source" for a work in object code form means all
268+the source code needed to generate, install, and (for an executable
269+work) run the object code and to modify the work, including scripts to
270+control those activities. However, it does not include the work's
271+System Libraries, or general-purpose tools or generally available free
272+programs which are used unmodified in performing those activities but
273+which are not part of the work. For example, Corresponding Source
274+includes interface definition files associated with source files for
275+the work, and the source code for shared libraries and dynamically
276+linked subprograms that the work is specifically designed to require,
277+such as by intimate data communication or control flow between those
278+subprograms and other parts of the work.
279+
280+ The Corresponding Source need not include anything that users
281+can regenerate automatically from other parts of the Corresponding
282+Source.
283+
284+ The Corresponding Source for a work in source code form is that
285+same work.
286+
287+ 2. Basic Permissions.
288+
289+ All rights granted under this License are granted for the term of
290+copyright on the Program, and are irrevocable provided the stated
291+conditions are met. This License explicitly affirms your unlimited
292+permission to run the unmodified Program. The output from running a
293+covered work is covered by this License only if the output, given its
294+content, constitutes a covered work. This License acknowledges your
295+rights of fair use or other equivalent, as provided by copyright law.
296+
297+ You may make, run and propagate covered works that you do not
298+convey, without conditions so long as your license otherwise remains
299+in force. You may convey covered works to others for the sole purpose
300+of having them make modifications exclusively for you, or provide you
301+with facilities for running those works, provided that you comply with
302+the terms of this License in conveying all material for which you do
303+not control copyright. Those thus making or running the covered works
304+for you must do so exclusively on your behalf, under your direction
305+and control, on terms that prohibit them from making any copies of
306+your copyrighted material outside their relationship with you.
307+
308+ Conveying under any other circumstances is permitted solely under
309+the conditions stated below. Sublicensing is not allowed; section 10
310+makes it unnecessary.
311+
312+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
313+
314+ No covered work shall be deemed part of an effective technological
315+measure under any applicable law fulfilling obligations under article
316+11 of the WIPO copyright treaty adopted on 20 December 1996, or
317+similar laws prohibiting or restricting circumvention of such
318+measures.
319+
320+ When you convey a covered work, you waive any legal power to forbid
321+circumvention of technological measures to the extent such circumvention
322+is effected by exercising rights under this License with respect to
323+the covered work, and you disclaim any intention to limit operation or
324+modification of the work as a means of enforcing, against the work's
325+users, your or third parties' legal rights to forbid circumvention of
326+technological measures.
327+
328+ 4. Conveying Verbatim Copies.
329+
330+ You may convey verbatim copies of the Program's source code as you
331+receive it, in any medium, provided that you conspicuously and
332+appropriately publish on each copy an appropriate copyright notice;
333+keep intact all notices stating that this License and any
334+non-permissive terms added in accord with section 7 apply to the code;
335+keep intact all notices of the absence of any warranty; and give all
336+recipients a copy of this License along with the Program.
337+
338+ You may charge any price or no price for each copy that you convey,
339+and you may offer support or warranty protection for a fee.
340+
341+ 5. Conveying Modified Source Versions.
342+
343+ You may convey a work based on the Program, or the modifications to
344+produce it from the Program, in the form of source code under the
345+terms of section 4, provided that you also meet all of these conditions:
346+
347+ a) The work must carry prominent notices stating that you modified
348+ it, and giving a relevant date.
349+
350+ b) The work must carry prominent notices stating that it is
351+ released under this License and any conditions added under section
352+ 7. This requirement modifies the requirement in section 4 to
353+ "keep intact all notices".
354+
355+ c) You must license the entire work, as a whole, under this
356+ License to anyone who comes into possession of a copy. This
357+ License will therefore apply, along with any applicable section 7
358+ additional terms, to the whole of the work, and all its parts,
359+ regardless of how they are packaged. This License gives no
360+ permission to license the work in any other way, but it does not
361+ invalidate such permission if you have separately received it.
362+
363+ d) If the work has interactive user interfaces, each must display
364+ Appropriate Legal Notices; however, if the Program has interactive
365+ interfaces that do not display Appropriate Legal Notices, your
366+ work need not make them do so.
367+
368+ A compilation of a covered work with other separate and independent
369+works, which are not by their nature extensions of the covered work,
370+and which are not combined with it such as to form a larger program,
371+in or on a volume of a storage or distribution medium, is called an
372+"aggregate" if the compilation and its resulting copyright are not
373+used to limit the access or legal rights of the compilation's users
374+beyond what the individual works permit. Inclusion of a covered work
375+in an aggregate does not cause this License to apply to the other
376+parts of the aggregate.
377+
378+ 6. Conveying Non-Source Forms.
379+
380+ You may convey a covered work in object code form under the terms
381+of sections 4 and 5, provided that you also convey the
382+machine-readable Corresponding Source under the terms of this License,
383+in one of these ways:
384+
385+ a) Convey the object code in, or embodied in, a physical product
386+ (including a physical distribution medium), accompanied by the
387+ Corresponding Source fixed on a durable physical medium
388+ customarily used for software interchange.
389+
390+ b) Convey the object code in, or embodied in, a physical product
391+ (including a physical distribution medium), accompanied by a
392+ written offer, valid for at least three years and valid for as
393+ long as you offer spare parts or customer support for that product
394+ model, to give anyone who possesses the object code either (1) a
395+ copy of the Corresponding Source for all the software in the
396+ product that is covered by this License, on a durable physical
397+ medium customarily used for software interchange, for a price no
398+ more than your reasonable cost of physically performing this
399+ conveying of source, or (2) access to copy the
400+ Corresponding Source from a network server at no charge.
401+
402+ c) Convey individual copies of the object code with a copy of the
403+ written offer to provide the Corresponding Source. This
404+ alternative is allowed only occasionally and noncommercially, and
405+ only if you received the object code with such an offer, in accord
406+ with subsection 6b.
407+
408+ d) Convey the object code by offering access from a designated
409+ place (gratis or for a charge), and offer equivalent access to the
410+ Corresponding Source in the same way through the same place at no
411+ further charge. You need not require recipients to copy the
412+ Corresponding Source along with the object code. If the place to
413+ copy the object code is a network server, the Corresponding Source
414+ may be on a different server (operated by you or a third party)
415+ that supports equivalent copying facilities, provided you maintain
416+ clear directions next to the object code saying where to find the
417+ Corresponding Source. Regardless of what server hosts the
418+ Corresponding Source, you remain obligated to ensure that it is
419+ available for as long as needed to satisfy these requirements.
420+
421+ e) Convey the object code using peer-to-peer transmission, provided
422+ you inform other peers where the object code and Corresponding
423+ Source of the work are being offered to the general public at no
424+ charge under subsection 6d.
425+
426+ A separable portion of the object code, whose source code is excluded
427+from the Corresponding Source as a System Library, need not be
428+included in conveying the object code work.
429+
430+ A "User Product" is either (1) a "consumer product", which means any
431+tangible personal property which is normally used for personal, family,
432+or household purposes, or (2) anything designed or sold for incorporation
433+into a dwelling. In determining whether a product is a consumer product,
434+doubtful cases shall be resolved in favor of coverage. For a particular
435+product received by a particular user, "normally used" refers to a
436+typical or common use of that class of product, regardless of the status
437+of the particular user or of the way in which the particular user
438+actually uses, or expects or is expected to use, the product. A product
439+is a consumer product regardless of whether the product has substantial
440+commercial, industrial or non-consumer uses, unless such uses represent
441+the only significant mode of use of the product.
442+
443+ "Installation Information" for a User Product means any methods,
444+procedures, authorization keys, or other information required to install
445+and execute modified versions of a covered work in that User Product from
446+a modified version of its Corresponding Source. The information must
447+suffice to ensure that the continued functioning of the modified object
448+code is in no case prevented or interfered with solely because
449+modification has been made.
450+
451+ If you convey an object code work under this section in, or with, or
452+specifically for use in, a User Product, and the conveying occurs as
453+part of a transaction in which the right of possession and use of the
454+User Product is transferred to the recipient in perpetuity or for a
455+fixed term (regardless of how the transaction is characterized), the
456+Corresponding Source conveyed under this section must be accompanied
457+by the Installation Information. But this requirement does not apply
458+if neither you nor any third party retains the ability to install
459+modified object code on the User Product (for example, the work has
460+been installed in ROM).
461+
462+ The requirement to provide Installation Information does not include a
463+requirement to continue to provide support service, warranty, or updates
464+for a work that has been modified or installed by the recipient, or for
465+the User Product in which it has been modified or installed. Access to a
466+network may be denied when the modification itself materially and
467+adversely affects the operation of the network or violates the rules and
468+protocols for communication across the network.
469+
470+ Corresponding Source conveyed, and Installation Information provided,
471+in accord with this section must be in a format that is publicly
472+documented (and with an implementation available to the public in
473+source code form), and must require no special password or key for
474+unpacking, reading or copying.
475+
476+ 7. Additional Terms.
477+
478+ "Additional permissions" are terms that supplement the terms of this
479+License by making exceptions from one or more of its conditions.
480+Additional permissions that are applicable to the entire Program shall
481+be treated as though they were included in this License, to the extent
482+that they are valid under applicable law. If additional permissions
483+apply only to part of the Program, that part may be used separately
484+under those permissions, but the entire Program remains governed by
485+this License without regard to the additional permissions.
486+
487+ When you convey a copy of a covered work, you may at your option
488+remove any additional permissions from that copy, or from any part of
489+it. (Additional permissions may be written to require their own
490+removal in certain cases when you modify the work.) You may place
491+additional permissions on material, added by you to a covered work,
492+for which you have or can give appropriate copyright permission.
493+
494+ Notwithstanding any other provision of this License, for material you
495+add to a covered work, you may (if authorized by the copyright holders of
496+that material) supplement the terms of this License with terms:
497+
498+ a) Disclaiming warranty or limiting liability differently from the
499+ terms of sections 15 and 16 of this License; or
500+
501+ b) Requiring preservation of specified reasonable legal notices or
502+ author attributions in that material or in the Appropriate Legal
503+ Notices displayed by works containing it; or
504+
505+ c) Prohibiting misrepresentation of the origin of that material, or
506+ requiring that modified versions of such material be marked in
507+ reasonable ways as different from the original version; or
508+
509+ d) Limiting the use for publicity purposes of names of licensors or
510+ authors of the material; or
511+
512+ e) Declining to grant rights under trademark law for use of some
513+ trade names, trademarks, or service marks; or
514+
515+ f) Requiring indemnification of licensors and authors of that
516+ material by anyone who conveys the material (or modified versions of
517+ it) with contractual assumptions of liability to the recipient, for
518+ any liability that these contractual assumptions directly impose on
519+ those licensors and authors.
520+
521+ All other non-permissive additional terms are considered "further
522+restrictions" within the meaning of section 10. If the Program as you
523+received it, or any part of it, contains a notice stating that it is
524+governed by this License along with a term that is a further
525+restriction, you may remove that term. If a license document contains
526+a further restriction but permits relicensing or conveying under this
527+License, you may add to a covered work material governed by the terms
528+of that license document, provided that the further restriction does
529+not survive such relicensing or conveying.
530+
531+ If you add terms to a covered work in accord with this section, you
532+must place, in the relevant source files, a statement of the
533+additional terms that apply to those files, or a notice indicating
534+where to find the applicable terms.
535+
536+ Additional terms, permissive or non-permissive, may be stated in the
537+form of a separately written license, or stated as exceptions;
538+the above requirements apply either way.
539+
540+ 8. Termination.
541+
542+ You may not propagate or modify a covered work except as expressly
543+provided under this License. Any attempt otherwise to propagate or
544+modify it is void, and will automatically terminate your rights under
545+this License (including any patent licenses granted under the third
546+paragraph of section 11).
547+
548+ However, if you cease all violation of this License, then your
549+license from a particular copyright holder is reinstated (a)
550+provisionally, unless and until the copyright holder explicitly and
551+finally terminates your license, and (b) permanently, if the copyright
552+holder fails to notify you of the violation by some reasonable means
553+prior to 60 days after the cessation.
554+
555+ Moreover, your license from a particular copyright holder is
556+reinstated permanently if the copyright holder notifies you of the
557+violation by some reasonable means, this is the first time you have
558+received notice of violation of this License (for any work) from that
559+copyright holder, and you cure the violation prior to 30 days after
560+your receipt of the notice.
561+
562+ Termination of your rights under this section does not terminate the
563+licenses of parties who have received copies or rights from you under
564+this License. If your rights have been terminated and not permanently
565+reinstated, you do not qualify to receive new licenses for the same
566+material under section 10.
567+
568+ 9. Acceptance Not Required for Having Copies.
569+
570+ You are not required to accept this License in order to receive or
571+run a copy of the Program. Ancillary propagation of a covered work
572+occurring solely as a consequence of using peer-to-peer transmission
573+to receive a copy likewise does not require acceptance. However,
574+nothing other than this License grants you permission to propagate or
575+modify any covered work. These actions infringe copyright if you do
576+not accept this License. Therefore, by modifying or propagating a
577+covered work, you indicate your acceptance of this License to do so.
578+
579+ 10. Automatic Licensing of Downstream Recipients.
580+
581+ Each time you convey a covered work, the recipient automatically
582+receives a license from the original licensors, to run, modify and
583+propagate that work, subject to this License. You are not responsible
584+for enforcing compliance by third parties with this License.
585+
586+ An "entity transaction" is a transaction transferring control of an
587+organization, or substantially all assets of one, or subdividing an
588+organization, or merging organizations. If propagation of a covered
589+work results from an entity transaction, each party to that
590+transaction who receives a copy of the work also receives whatever
591+licenses to the work the party's predecessor in interest had or could
592+give under the previous paragraph, plus a right to possession of the
593+Corresponding Source of the work from the predecessor in interest, if
594+the predecessor has it or can get it with reasonable efforts.
595+
596+ You may not impose any further restrictions on the exercise of the
597+rights granted or affirmed under this License. For example, you may
598+not impose a license fee, royalty, or other charge for exercise of
599+rights granted under this License, and you may not initiate litigation
600+(including a cross-claim or counterclaim in a lawsuit) alleging that
601+any patent claim is infringed by making, using, selling, offering for
602+sale, or importing the Program or any portion of it.
603+
604+ 11. Patents.
605+
606+ A "contributor" is a copyright holder who authorizes use under this
607+License of the Program or a work on which the Program is based. The
608+work thus licensed is called the contributor's "contributor version".
609+
610+ A contributor's "essential patent claims" are all patent claims
611+owned or controlled by the contributor, whether already acquired or
612+hereafter acquired, that would be infringed by some manner, permitted
613+by this License, of making, using, or selling its contributor version,
614+but do not include claims that would be infringed only as a
615+consequence of further modification of the contributor version. For
616+purposes of this definition, "control" includes the right to grant
617+patent sublicenses in a manner consistent with the requirements of
618+this License.
619+
620+ Each contributor grants you a non-exclusive, worldwide, royalty-free
621+patent license under the contributor's essential patent claims, to
622+make, use, sell, offer for sale, import and otherwise run, modify and
623+propagate the contents of its contributor version.
624+
625+ In the following three paragraphs, a "patent license" is any express
626+agreement or commitment, however denominated, not to enforce a patent
627+(such as an express permission to practice a patent or covenant not to
628+sue for patent infringement). To "grant" such a patent license to a
629+party means to make such an agreement or commitment not to enforce a
630+patent against the party.
631+
632+ If you convey a covered work, knowingly relying on a patent license,
633+and the Corresponding Source of the work is not available for anyone
634+to copy, free of charge and under the terms of this License, through a
635+publicly available network server or other readily accessible means,
636+then you must either (1) cause the Corresponding Source to be so
637+available, or (2) arrange to deprive yourself of the benefit of the
638+patent license for this particular work, or (3) arrange, in a manner
639+consistent with the requirements of this License, to extend the patent
640+license to downstream recipients. "Knowingly relying" means you have
641+actual knowledge that, but for the patent license, your conveying the
642+covered work in a country, or your recipient's use of the covered work
643+in a country, would infringe one or more identifiable patents in that
644+country that you have reason to believe are valid.
645+
646+ If, pursuant to or in connection with a single transaction or
647+arrangement, you convey, or propagate by procuring conveyance of, a
648+covered work, and grant a patent license to some of the parties
649+receiving the covered work authorizing them to use, propagate, modify
650+or convey a specific copy of the covered work, then the patent license
651+you grant is automatically extended to all recipients of the covered
652+work and works based on it.
653+
654+ A patent license is "discriminatory" if it does not include within
655+the scope of its coverage, prohibits the exercise of, or is
656+conditioned on the non-exercise of one or more of the rights that are
657+specifically granted under this License. You may not convey a covered
658+work if you are a party to an arrangement with a third party that is
659+in the business of distributing software, under which you make payment
660+to the third party based on the extent of your activity of conveying
661+the work, and under which the third party grants, to any of the
662+parties who would receive the covered work from you, a discriminatory
663+patent license (a) in connection with copies of the covered work
664+conveyed by you (or copies made from those copies), or (b) primarily
665+for and in connection with specific products or compilations that
666+contain the covered work, unless you entered into that arrangement,
667+or that patent license was granted, prior to 28 March 2007.
668+
669+ Nothing in this License shall be construed as excluding or limiting
670+any implied license or other defenses to infringement that may
671+otherwise be available to you under applicable patent law.
672+
673+ 12. No Surrender of Others' Freedom.
674+
675+ If conditions are imposed on you (whether by court order, agreement or
676+otherwise) that contradict the conditions of this License, they do not
677+excuse you from the conditions of this License. If you cannot convey a
678+covered work so as to satisfy simultaneously your obligations under this
679+License and any other pertinent obligations, then as a consequence you may
680+not convey it at all. For example, if you agree to terms that obligate you
681+to collect a royalty for further conveying from those to whom you convey
682+the Program, the only way you could satisfy both those terms and this
683+License would be to refrain entirely from conveying the Program.
684+
685+ 13. Use with the GNU Affero General Public License.
686+
687+ Notwithstanding any other provision of this License, you have
688+permission to link or combine any covered work with a work licensed
689+under version 3 of the GNU Affero General Public License into a single
690+combined work, and to convey the resulting work. The terms of this
691+License will continue to apply to the part which is the covered work,
692+but the special requirements of the GNU Affero General Public License,
693+section 13, concerning interaction through a network will apply to the
694+combination as such.
695+
696+ 14. Revised Versions of this License.
697+
698+ The Free Software Foundation may publish revised and/or new versions of
699+the GNU General Public License from time to time. Such new versions will
700+be similar in spirit to the present version, but may differ in detail to
701+address new problems or concerns.
702+
703+ Each version is given a distinguishing version number. If the
704+Program specifies that a certain numbered version of the GNU General
705+Public License "or any later version" applies to it, you have the
706+option of following the terms and conditions either of that numbered
707+version or of any later version published by the Free Software
708+Foundation. If the Program does not specify a version number of the
709+GNU General Public License, you may choose any version ever published
710+by the Free Software Foundation.
711+
712+ If the Program specifies that a proxy can decide which future
713+versions of the GNU General Public License can be used, that proxy's
714+public statement of acceptance of a version permanently authorizes you
715+to choose that version for the Program.
716+
717+ Later license versions may give you additional or different
718+permissions. However, no additional obligations are imposed on any
719+author or copyright holder as a result of your choosing to follow a
720+later version.
721+
722+ 15. Disclaimer of Warranty.
723+
724+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
725+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
726+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
727+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
728+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
729+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
730+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
731+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
732+
733+ 16. Limitation of Liability.
734+
735+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
736+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
737+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
738+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
739+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
740+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
741+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
742+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
743+SUCH DAMAGES.
744+
745+ 17. Interpretation of Sections 15 and 16.
746+
747+ If the disclaimer of warranty and limitation of liability provided
748+above cannot be given local legal effect according to their terms,
749+reviewing courts shall apply local law that most closely approximates
750+an absolute waiver of all civil liability in connection with the
751+Program, unless a warranty or assumption of liability accompanies a
752+copy of the Program in return for a fee.
753+
754+ END OF TERMS AND CONDITIONS
755+
756+ How to Apply These Terms to Your New Programs
757+
758+ If you develop a new program, and you want it to be of the greatest
759+possible use to the public, the best way to achieve this is to make it
760+free software which everyone can redistribute and change under these terms.
761+
762+ To do so, attach the following notices to the program. It is safest
763+to attach them to the start of each source file to most effectively
764+state the exclusion of warranty; and each file should have at least
765+the "copyright" line and a pointer to where the full notice is found.
766+
767+ <one line to give the program's name and a brief idea of what it does.>
768+ Copyright (C) <year> <name of author>
769+
770+ This program is free software: you can redistribute it and/or modify
771+ it under the terms of the GNU General Public License as published by
772+ the Free Software Foundation, either version 3 of the License, or
773+ (at your option) any later version.
774+
775+ This program is distributed in the hope that it will be useful,
776+ but WITHOUT ANY WARRANTY; without even the implied warranty of
777+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
778+ GNU General Public License for more details.
779+
780+ You should have received a copy of the GNU General Public License
781+ along with this program. If not, see <http://www.gnu.org/licenses/>.
782+
783+Also add information on how to contact you by electronic and paper mail.
784+
785+ If the program does terminal interaction, make it output a short
786+notice like this when it starts in an interactive mode:
787+
788+ <program> Copyright (C) <year> <name of author>
789+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
790+ This is free software, and you are welcome to redistribute it
791+ under certain conditions; type `show c' for details.
792+
793+The hypothetical commands `show w' and `show c' should show the appropriate
794+parts of the General Public License. Of course, your program's commands
795+might be different; for a GUI interface, you would use an "about box".
796+
797+ You should also get your employer (if you work as a programmer) or school,
798+if any, to sign a "copyright disclaimer" for the program, if necessary.
799+For more information on this, and how to apply and follow the GNU GPL, see
800+<http://www.gnu.org/licenses/>.
801+
802+ The GNU General Public License does not permit incorporating your program
803+into proprietary programs. If your program is a subroutine library, you
804+may consider it more useful to permit linking proprietary applications with
805+the library. If this is what you want to do, use the GNU Lesser General
806+Public License instead of this License. But first, please read
807+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
808
809=== added file 'MANIFEST.in'
810--- MANIFEST.in 1970-01-01 00:00:00 +0000
811+++ MANIFEST.in 2013-10-01 18:38:21 +0000
812@@ -0,0 +1,1 @@
813+include checkbox/parsers/tests/pactl_data/*.txt
814
815=== added file 'README'
816--- README 1970-01-01 00:00:00 +0000
817+++ README 2013-10-01 18:38:21 +0000
818@@ -0,0 +1,5 @@
819+This is Checkbox which provides an extensible interface for system testing.
820+
821+See the following page for documentation:
822+
823+https://wiki.ubuntu.com/Testing/Automation/Checkbox
824
825=== added file 'README-PORT'
826--- README-PORT 1970-01-01 00:00:00 +0000
827+++ README-PORT 2013-10-01 18:38:21 +0000
828@@ -0,0 +1,76 @@
829+The following steps will port your Checkbox source from Ubuntu to CentOS 6.4.
830+
831+Note: Following steps are on an Ubuntu system.
832+1) Install the devscripts package to create a debian package from source.
833+ # apt-get install devscripts
834+
835+2) Perform the following command in the Checkbox source code root directory to create
836+ a debian package.
837+ /checkbox# debuild -i -us -uc -b
838+
839+3) Move up to the parent directory to view the debian packages that were built:
840+ # cd ..
841+
842+4) Download alien to convert the .deb packages into .rpm packages.
843+ # apt-get install alien
844+
845+5) Convert the .deb packages into .rpm packages using the following command:
846+ alien -r package_name.deb --scripts
847+
848+6) Transfer the checkbox*.rpm files to the CentOS 6.4 machine.
849+
850+Note: Following steps are on a CentOS 6.4 system.
851+7) Install python3 on the CentOS machnine.
852+ 7a) Install Development tools to be able to build from source
853+ # yum groupinstall "Development tools"
854+
855+ 7b) Install extra libraries for the python3 interpreter
856+ # yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqllite-devel readline-devel tk-devel
857+
858+ 7c) Add the Princeton University and the Institute for Advanced Study (PUIAS) repository to your mirrorlist to install python3.
859+ i. Download the rpm gpg key
860+ # cd /etc/pki/rpm-gpg
861+ # wget -q http://springdale.math.ias.edu/data/puias/6/x86_64/os/RPM-GPG-KEY-puias
862+ # rpm --import RPM-GPG-KEY-puias
863+ ii. Create the repository file as /etc/yum.repos.d/puias-computational.repo
864+ # cd /etc/yum.repos.d
865+ # touch puias-computational.repo
866+ # editor(vim, nano, etc) puias-computational.repo
867+ [PUIAS_6_computational]
868+ name=PUIAS computational BASE $releasever - $basearch
869+ mirrorlist=http://puias.math.ias.edu/data/puias/computational/$releasever/$basearch/mirrorlist
870+ #baseurl=http://puias.math.ias.edu/data/puias/computational/$releasever/$basearch
871+ gpgcheck=1
872+ gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puias
873+
874+ 7d) Search for python3 in your repo.
875+ # yum search python3
876+
877+ 7e) Install python3 if it is returned after the search.
878+ # yum install python3
879+
880+8) Install Distribute to allow python modules to be installed
881+ 8a) Download Distribute source code.
882+ # wget http://pypi.python.org/packages/source/d/distribute/distribute-0.6.35.tar.gz
883+
884+ 8b) Extract and Install
885+ # tar -xzvf distribute-0.6.35.tar.gz
886+ # cd distribute-0.6.35
887+ # python3 setup.py build && python3 setup.py install
888+
889+9) Install the lxml python module that is needed by checkbox
890+ 9a) Install lxml dependencies
891+ # yum install libxml2-devel libxslt-devel
892+
893+ 9b) Install module
894+ # easy_install-3.3 lxml
895+
896+10) Install the checkbox*.rpm files in the directory they were stored in.
897+ # rpm -Uvh checkbox*.rpm
898+
899+11) Copy the checkbox files from /usr/lib/python3/dist-packages into /usr/lib/python3.3/site-packages/ or else checkbox won't run.
900+ # cp -r /usr/lib/python3/dist-packages/checkbox* /usr/lib/python3.3/site-packages/
901+
902+12) Run checkbox at the terminal with the following command
903+ # checkbox-ocp
904+
905
906=== added file 'Vagrantfile'
907--- Vagrantfile 1970-01-01 00:00:00 +0000
908+++ Vagrantfile 2013-10-01 18:38:21 +0000
909@@ -0,0 +1,56 @@
910+# -*- mode: ruby -*-
911+# vi: set ft=ruby sw=2 ts=2 :
912+
913+Vagrant::Config.run do |config|
914+
915+ config.ssh.timeout = 60
916+
917+ # Define a Ubuntu Server image (cloud) for the 12.04 release (precise)
918+ config.vm.define :precise do |precise_config|
919+ precise_config.vm.box = "precise-cloud-i386"
920+ precise_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-i386-vagrant-disk1.box"
921+ end
922+
923+ # Define a Ubuntu Server image (cloud) for the 12.10 release (quantal)
924+ config.vm.define :quantal do |quantal_config|
925+ quantal_config.vm.box = "quantal-cloud-i386"
926+ quantal_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/quantal/current/quantal-server-cloudimg-i386-vagrant-disk1.box"
927+ end
928+
929+ # Define a Ubuntu Server image (cloud) for the 13.04 release (raring)
930+ config.vm.define :raring do |raring_config|
931+ raring_config.vm.box = "raring-cloud-i386"
932+ raring_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/raring/current/raring-server-cloudimg-i386-vagrant-disk1.box"
933+ end
934+
935+ # For debugging and later future GUI testing
936+ if ENV.key? "VAGRANT_GUI"
937+ config.vm.boot_mode = :gui
938+ end
939+
940+ # Setup an apt cache if one is available
941+ if ENV.key? "VAGRANT_APT_CACHE"
942+ config.vm.provision :shell, :inline => "echo 'Acquire::http { Proxy \"#{ENV['VAGRANT_APT_CACHE']}\"; };' > /etc/apt/apt.conf"
943+ end
944+
945+ # Update to have the latest packages, this is needed because the image comes
946+ # with an old (and no longer working) apt cache and links to many packages no
947+ # longer work.
948+ config.vm.provision :shell, :inline => "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade --yes"
949+ # Install dependencies from native packages
950+ config.vm.provision :shell, :inline => "apt-get install --yes python3-setuptools python3-pkg-resources python3-lxml"
951+ # Install python3-mock so that we can create mock objects for testing
952+ config.vm.provision :shell, :inline => "apt-get install --yes python3-mock"
953+ # Install python3-sphinx so that we can build documentation
954+ config.vm.provision :shell, :inline => "apt-get install --yes python3-sphinx"
955+ # Install policykit-1 so that we have pkexec
956+ config.vm.provision :shell, :inline => "apt-get install --yes policykit-1"
957+ # Install some checkbox script dependencies:
958+ # Later on those could be installed on demand to test how we behave without
959+ # them but for now that's good enough. Little by little...
960+ config.vm.provision :shell, :inline => "apt-get install --yes fwts"
961+ # Develop plainbox so that we have it in $PATH
962+ config.vm.provision :shell, :inline => "cd /vagrant/plainbox/ && python3 setup.py develop"
963+ # Create a cool symlink so that everyone knows where to go to
964+ config.vm.provision :shell, :inline => "ln --no-dereference --force --symbolic /vagrant /home/vagrant/checkbox"
965+end
966
967=== added directory 'apport'
968=== added file 'apport/checkbox.py'
969--- apport/checkbox.py 1970-01-01 00:00:00 +0000
970+++ apport/checkbox.py 2013-10-01 18:38:21 +0000
971@@ -0,0 +1,24 @@
972+'''apport general hook for checkbox
973+
974+(c) 2009 Canonical Ltd.
975+Author: David Murphy <schwuk@ubuntu.com>
976+'''
977+import os
978+
979+
980+def add_info(report):
981+ HOME = os.environ.get('HOME', '/')
982+ CACHE_HOME = os.environ.get('XDG_CACHE_HOME',
983+ os.path.join(HOME, '.cache'))
984+ SYSTEM = os.path.join(CACHE_HOME, 'checkbox', 'system')
985+ SUBMISSION = os.path.join(CACHE_HOME, 'checkbox', 'submission')
986+
987+ try:
988+ report['CheckboxSystem'] = open(SYSTEM).read()
989+ except IOError:
990+ pass
991+
992+ try:
993+ report['CheckboxSubmission'] = open(SUBMISSION).read()
994+ except IOError:
995+ pass
996
997=== added file 'apport/source_checkbox.py'
998--- apport/source_checkbox.py 1970-01-01 00:00:00 +0000
999+++ apport/source_checkbox.py 2013-10-01 18:38:21 +0000
1000@@ -0,0 +1,20 @@
1001+'''apport package hook for checkbox
1002+
1003+(c) 2009 Canonical Ltd.
1004+Author: David Murphy <schwuk@ubuntu.com>
1005+'''
1006+import os
1007+
1008+from apport.hookutils import attach_file_if_exists
1009+
1010+
1011+def add_info(report):
1012+ HOME = os.environ.get('HOME', '/')
1013+ CACHE_HOME = os.environ.get('XDG_CACHE_HOME',
1014+ os.path.join(HOME, '.cache'))
1015+
1016+ SUBMISSION = os.path.join(CACHE_HOME, 'checkbox', 'submission.xml')
1017+ attach_file_if_exists(report, SUBMISSION)
1018+
1019+ CHECKBOX_LOG = os.path.join(CACHE_HOME, 'checkbox', 'checkbox.log')
1020+ attach_file_if_exists(report, CHECKBOX_LOG)
1021
1022=== added file 'backend'
1023--- backend 1970-01-01 00:00:00 +0000
1024+++ backend 2013-10-01 18:38:21 +0000
1025@@ -0,0 +1,66 @@
1026+#!/usr/bin/python3
1027+
1028+import os
1029+import sys
1030+
1031+from optparse import OptionParser
1032+from checkbox.lib.fifo import FifoReader, FifoWriter
1033+
1034+from checkbox.job import Job, FAIL
1035+
1036+
1037+def main(args):
1038+ usage = "Usage: %prog INPUT OUTPUT"
1039+ parser = OptionParser(usage=usage)
1040+ parser.add_option("-p", "--path",
1041+ help="PATH variable to replace in the environment")
1042+ (options, args) = parser.parse_args(args)
1043+
1044+ if len(args) < 2:
1045+ parser.error("Missing INPUT and OUTPUT")
1046+
1047+ # Set PATH
1048+ if options.path:
1049+ os.environ["PATH"] = options.path
1050+
1051+ # Set PYTHONPATH
1052+ pythonpath = sys.path[0]
1053+ if "PYTHONPATH" in os.environ:
1054+ pythonpath = ":".join([pythonpath, os.environ["PYTHONPATH"]])
1055+ os.environ["PYTHONPATH"] = pythonpath
1056+
1057+ reader = FifoReader(args[0])
1058+ writer = FifoWriter(args[1])
1059+
1060+ while True:
1061+ try:
1062+ message = reader.read_object()
1063+ #"unpack" the message
1064+ sequence, message = message
1065+ if message == "stop":
1066+ break
1067+ if message == "ping":
1068+ #Build a tuple with the sequence number as
1069+ #received
1070+ writer.write_object((sequence, "pong",))
1071+ continue
1072+ if isinstance(message, dict) and "command" in message:
1073+ job = Job(message["command"], message.get("environ"),
1074+ message.get("timeout"))
1075+ status, data, duration = job.execute()
1076+ try:
1077+ data = data.decode("utf-8")
1078+ except UnicodeDecodeError:
1079+ status, data, duration = (FAIL, "Decode error", 0,)
1080+ else:
1081+ status, data, duration = (FAIL, "", 0,)
1082+ #Build a tuple with sequence number
1083+ writer.write_object((sequence, (status, data, duration,),))
1084+ except IOError:
1085+ break
1086+
1087+ return 0
1088+
1089+
1090+if __name__ == "__main__":
1091+ sys.exit(main(sys.argv[1:]))
1092
1093=== added directory 'bin'
1094=== added file 'bin/checkbox-hw-collection'
1095--- bin/checkbox-hw-collection 1970-01-01 00:00:00 +0000
1096+++ bin/checkbox-hw-collection 2013-10-01 18:38:21 +0000
1097@@ -0,0 +1,19 @@
1098+#!/bin/bash
1099+
1100+set -e
1101+
1102+export CHECKBOX_DATA=${CHECKBOX_DATA:-~/.checkbox-hw-collection}
1103+export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
1104+export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/hwsubmit.whitelist}
1105+export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
1106+
1107+if [ $CHECKBOX_DATA != '.' ]
1108+then
1109+ old_data=$HOME/.checkbox-hw-collection
1110+ if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
1111+ then
1112+ mv -f $old_data $CHECKBOX_DATA
1113+ fi
1114+fi
1115+
1116+python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
1117
1118=== added file 'bin/checkbox-ocp-cli'
1119--- bin/checkbox-ocp-cli 1970-01-01 00:00:00 +0000
1120+++ bin/checkbox-ocp-cli 2013-10-01 18:38:21 +0000
1121@@ -0,0 +1,22 @@
1122+#!/bin/bash
1123+
1124+export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
1125+export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
1126+export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
1127+export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
1128+export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
1129+
1130+if ( ! grep -q "Ubuntu" /etc/issue ) && [ ! -L /etc/checkbox.d/configs ]
1131+then ln -s /usr/share/checkbox/examples /etc/checkbox.d/configs
1132+fi
1133+
1134+if [ $CHECKBOX_DATA != '.' ]
1135+then
1136+ old_data=$HOME/.checkbox
1137+ if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
1138+ then
1139+ mv -f $old_data $CHECKBOX_DATA
1140+ fi
1141+fi
1142+
1143+python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
1144
1145=== added file 'bin/checkbox-ocp-gtk'
1146--- bin/checkbox-ocp-gtk 1970-01-01 00:00:00 +0000
1147+++ bin/checkbox-ocp-gtk 2013-10-01 18:38:21 +0000
1148@@ -0,0 +1,22 @@
1149+#!/bin/bash
1150+
1151+export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
1152+export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
1153+export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
1154+export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
1155+export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
1156+
1157+if ( ! grep -q "Ubuntu" /etc/issue ) && [ ! -L /etc/checkbox.d/configs ]
1158+then ln -s /usr/share/checkbox/examples /etc/checkbox.d/configs
1159+fi
1160+
1161+if [ $CHECKBOX_DATA != '.' ]
1162+then
1163+ old_data=$HOME/.checkbox
1164+ if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
1165+ then
1166+ mv -f $old_data $CHECKBOX_DATA
1167+ fi
1168+fi
1169+
1170+python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
1171
1172=== added file 'bin/checkbox-ocp-qt'
1173--- bin/checkbox-ocp-qt 1970-01-01 00:00:00 +0000
1174+++ bin/checkbox-ocp-qt 2013-10-01 18:38:21 +0000
1175@@ -0,0 +1,22 @@
1176+#!/bin/bash
1177+
1178+export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
1179+export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
1180+export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
1181+export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
1182+export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
1183+
1184+if ( ! grep -q "Ubuntu" /etc/issue ) && [ ! -L /etc/checkbox.d/configs ]
1185+then ln -s /usr/share/checkbox/examples /etc/checkbox.d/configs
1186+fi
1187+
1188+if [ $CHECKBOX_DATA != '.' ]
1189+then
1190+ old_data=$HOME/.checkbox
1191+ if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
1192+ then
1193+ mv -f $old_data $CHECKBOX_DATA
1194+ fi
1195+fi
1196+
1197+python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
1198
1199=== added file 'bin/checkbox-ocp-urwid'
1200--- bin/checkbox-ocp-urwid 1970-01-01 00:00:00 +0000
1201+++ bin/checkbox-ocp-urwid 2013-10-01 18:38:21 +0000
1202@@ -0,0 +1,22 @@
1203+#!/bin/bash
1204+
1205+export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
1206+export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
1207+export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
1208+export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
1209+export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
1210+
1211+if ( ! grep -q "Ubuntu" /etc/issue ) && [ ! -L /etc/checkbox.d/configs ]
1212+then ln -s /usr/share/checkbox/examples /etc/checkbox.d/configs
1213+fi
1214+
1215+if [ $CHECKBOX_DATA != '.' ]
1216+then
1217+ old_data=$HOME/.checkbox
1218+ if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
1219+ then
1220+ mv -f $old_data $CHECKBOX_DATA
1221+ fi
1222+fi
1223+
1224+python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
1225
1226=== added directory 'checkbox'
1227=== added file 'checkbox/__init__.py'
1228=== added file 'checkbox/application.py'
1229--- checkbox/application.py 1970-01-01 00:00:00 +0000
1230+++ checkbox/application.py 2013-10-01 18:38:21 +0000
1231@@ -0,0 +1,136 @@
1232+#
1233+# This file is part of Checkbox.
1234+#
1235+# Copyright 2008 Canonical Ltd.
1236+#
1237+# Checkbox is free software: you can redistribute it and/or modify
1238+# it under the terms of the GNU General Public License as published by
1239+# the Free Software Foundation, either version 3 of the License, or
1240+# (at your option) any later version.
1241+#
1242+# Checkbox is distributed in the hope that it will be useful,
1243+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1244+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1245+# GNU General Public License for more details.
1246+#
1247+# You should have received a copy of the GNU General Public License
1248+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1249+#
1250+import os
1251+import sys
1252+import logging
1253+import posixpath
1254+
1255+from gettext import gettext as _
1256+
1257+from optparse import OptionParser
1258+
1259+from checkbox.lib.config import Config
1260+from checkbox.lib.environ import get_variable
1261+from checkbox.lib.log import set_logging
1262+from checkbox.lib.safe import safe_make_directory
1263+from checkbox.lib.text import split
1264+
1265+from checkbox.plugin import PluginManager
1266+from checkbox.reactor import Reactor
1267+
1268+
1269+class Application:
1270+
1271+ reactor_factory = Reactor
1272+
1273+ def __init__(self, config):
1274+ self._config = config
1275+ self.reactor = self.reactor_factory()
1276+
1277+ # Plugin manager setup
1278+ self.plugin_manager = PluginManager(self._config,
1279+ self.reactor)
1280+
1281+ def run(self):
1282+ try:
1283+ self.reactor.run()
1284+ except:
1285+ logging.exception("Error running reactor.")
1286+ raise
1287+
1288+
1289+class ApplicationManager:
1290+
1291+ application_factory = Application
1292+
1293+ default_log = os.path.join(get_variable("CHECKBOX_DATA", "."), "checkbox.log")
1294+ default_log_level = "info"
1295+
1296+ def parse_options(self, args):
1297+ usage = _("Usage: checkbox [OPTIONS]")
1298+ parser = OptionParser(usage=usage)
1299+ parser.add_option("--version",
1300+ action="store_true",
1301+ help=_("Print version information and exit."))
1302+ parser.add_option("-l", "--log",
1303+ metavar="FILE",
1304+ default=self.default_log,
1305+ help=_("The file to write the log to."))
1306+ parser.add_option("--log-level",
1307+ default=self.default_log_level,
1308+ help=_("One of debug, info, warning, error or critical."))
1309+ parser.add_option("-c", "--config",
1310+ action="append",
1311+ type="string",
1312+ default=[],
1313+ help=_("Configuration override parameters."))
1314+ parser.add_option("-b", "--blacklist",
1315+ help=_("Shorthand for --config=.*/jobs_info/blacklist."))
1316+ parser.add_option("-B", "--blacklist-file",
1317+ help=_("Shorthand for --config=.*/jobs_info/blacklist_file."))
1318+ parser.add_option("-w", "--whitelist",
1319+ help=_("Shorthand for --config=.*/jobs_info/whitelist."))
1320+ parser.add_option("-W", "--whitelist-file",
1321+ help=_("Shorthand for --config=.*/jobs_info/whitelist_file."))
1322+ return parser.parse_args(args)
1323+
1324+ def create_application(self, args=sys.argv):
1325+ # Create data directory
1326+ data_directory = get_variable("CHECKBOX_DATA", ".")
1327+ safe_make_directory(data_directory)
1328+
1329+ # Prepend environment options
1330+ string_options = get_variable("CHECKBOX_OPTIONS", "")
1331+ args[:0] = split(string_options)
1332+ (options, args) = self.parse_options(args)
1333+
1334+ # Replace shorthands
1335+ for shorthand in "blacklist", "blacklist_file", "whitelist", "whitelist_file":
1336+ key = ".*/jobs_info/%s" % shorthand
1337+ value = getattr(options, shorthand)
1338+ if value:
1339+ options.config.append("=".join([key, value]))
1340+
1341+ # Set logging early
1342+ set_logging(options.log_level, options.log)
1343+
1344+ # Config setup
1345+ if len(args) != 2:
1346+ sys.stderr.write(_("Missing configuration file as argument.\n"))
1347+ sys.exit(1)
1348+
1349+ config = Config()
1350+ config_filename = posixpath.expanduser(args[1])
1351+ config.read_filename(config_filename)
1352+ config.read_configs(options.config)
1353+
1354+ section_name = "checkbox/plugins/client_info"
1355+ section = config.get_section(section_name)
1356+ if not section:
1357+ section = config.add_section(section_name)
1358+ section.set("name", posixpath.basename(args[1]) \
1359+ .replace(".ini", ""))
1360+ section.set("version", config.get_defaults().version)
1361+
1362+ # Check options
1363+ if options.version:
1364+ print(config.get_defaults().version)
1365+ sys.exit(0)
1366+
1367+ return self.application_factory(config)
1368
1369=== added file 'checkbox/arguments.py'
1370--- checkbox/arguments.py 1970-01-01 00:00:00 +0000
1371+++ checkbox/arguments.py 2013-10-01 18:38:21 +0000
1372@@ -0,0 +1,95 @@
1373+#
1374+# This file is part of Checkbox.
1375+#
1376+# Copyright 2008 Canonical Ltd.
1377+#
1378+# Checkbox is free software: you can redistribute it and/or modify
1379+# it under the terms of the GNU General Public License as published by
1380+# the Free Software Foundation, either version 3 of the License, or
1381+# (at your option) any later version.
1382+#
1383+# Checkbox is distributed in the hope that it will be useful,
1384+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1385+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1386+# GNU General Public License for more details.
1387+#
1388+# You should have received a copy of the GNU General Public License
1389+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1390+#
1391+from inspect import getargspec
1392+
1393+from checkbox.lib.decorator import merge_function_metadata
1394+
1395+
1396+class ArgumentReplacer:
1397+ """A decorator helper that filters arguments to be passed to a function.
1398+
1399+ Create one with the original function and a function to replace
1400+ the arguments, and then call replace_arguments for each call to
1401+ the function.
1402+ """
1403+ def __init__(self, original_function, argument_replacer):
1404+ """
1405+ @param original_function: The function which will be called
1406+ soon. The function must *not* define any * or ** parameters.
1407+ @param argument_replacer: A function which will be called for
1408+ every argument. It will be passed a parameter name and the
1409+ associated argument. It should return the new value.
1410+ """
1411+ self.original_function = original_function
1412+ self.argument_replacer = argument_replacer
1413+
1414+ spec = getargspec(original_function)
1415+ self.all_arguments = spec[0]
1416+ if getattr(original_function, 'im_self', None) is not None:
1417+ # If it's bound, ignore the bound arguments.
1418+ self.all_arguments = self.all_arguments[1:]
1419+
1420+ def replace_arguments(self, args, kwargs):
1421+ """Filter some arguments destined to be passed to a function.
1422+
1423+ @param args: Original positional arguments.
1424+ @param kwargs: Original keyword arguments.
1425+
1426+ @return: new arguments and kwarguments.
1427+ """
1428+ args = list(args)
1429+ kwargs = kwargs.copy()
1430+
1431+ for name_index, name in enumerate(self.all_arguments):
1432+ # Ok, we've got the name of the argument. Let's find the value
1433+ # of the argument in our original arguments and replace it
1434+ # whether it's a positional or a keyword argument.
1435+ if name_index < len(args):
1436+ # Must be a positional argument
1437+ value = args[name_index]
1438+ args[name_index] = self.argument_replacer(name, value)
1439+ else:
1440+ # Must be a keyword argument
1441+ if name not in kwargs:
1442+ # Oh, but it wasn't passed in. Ignore it.
1443+ continue
1444+ value = kwargs[name]
1445+ kwargs[name] = self.argument_replacer(name, value)
1446+ return args, kwargs
1447+
1448+
1449+def coerce_arguments(**schemas):
1450+ """
1451+ A decorator factory which returns a decorator which coerces arguments.
1452+ """
1453+ def replacer(name, value):
1454+ if name in schemas:
1455+ return schemas[name].coerce(value)
1456+ return value
1457+
1458+ def decorator(original):
1459+ argument_replacer = ArgumentReplacer(original, replacer)
1460+ def replacement(*args, **kwargs):
1461+ new_args, new_kwargs = argument_replacer.replace_arguments(args,
1462+ kwargs)
1463+ return original(*new_args, **new_kwargs)
1464+ merge_function_metadata(original, replacement)
1465+ return replacement
1466+
1467+ return decorator
1468
1469=== added file 'checkbox/attribute.py'
1470--- checkbox/attribute.py 1970-01-01 00:00:00 +0000
1471+++ checkbox/attribute.py 2013-10-01 18:38:21 +0000
1472@@ -0,0 +1,41 @@
1473+#
1474+# This file is part of Checkbox.
1475+#
1476+# Copyright 2008 Canonical Ltd.
1477+#
1478+# Checkbox is free software: you can redistribute it and/or modify
1479+# it under the terms of the GNU General Public License as published by
1480+# the Free Software Foundation, either version 3 of the License, or
1481+# (at your option) any later version.
1482+#
1483+# Checkbox is distributed in the hope that it will be useful,
1484+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1485+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1486+# GNU General Public License for more details.
1487+#
1488+# You should have received a copy of the GNU General Public License
1489+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1490+#
1491+from checkbox.variables import Variable
1492+
1493+
1494+class Attribute:
1495+
1496+ def __init__(self, name, cls, variable_factory=None):
1497+ self.name = name
1498+ self.cls = cls
1499+ self.variable_factory = variable_factory or Variable
1500+
1501+
1502+def get_attributes(cls):
1503+ if "__checkbox_attributes__" in cls.__dict__:
1504+ return cls.__dict__["__checkbox_attributes__"]
1505+ else:
1506+ attributes = {}
1507+ for name in dir(cls):
1508+ attribute = getattr(cls, name, None)
1509+ if isinstance(attribute, Attribute):
1510+ attributes[name] = attribute
1511+
1512+ cls.__checkbox_attributes__ = attributes
1513+ return cls.__checkbox_attributes__
1514
1515=== added file 'checkbox/component.py'
1516--- checkbox/component.py 1970-01-01 00:00:00 +0000
1517+++ checkbox/component.py 2013-10-01 18:38:21 +0000
1518@@ -0,0 +1,184 @@
1519+#
1520+# This file is part of Checkbox.
1521+#
1522+# Copyright 2008 Canonical Ltd.
1523+#
1524+# Checkbox is free software: you can redistribute it and/or modify
1525+# it under the terms of the GNU General Public License as published by
1526+# the Free Software Foundation, either version 3 of the License, or
1527+# (at your option) any later version.
1528+#
1529+# Checkbox is distributed in the hope that it will be useful,
1530+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1531+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1532+# GNU General Public License for more details.
1533+#
1534+# You should have received a copy of the GNU General Public License
1535+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1536+#
1537+import os
1538+import re
1539+import itertools
1540+import logging
1541+import posixpath
1542+
1543+from checkbox.lib.cache import cache
1544+from checkbox.lib.path import path_expand_recursive
1545+
1546+from checkbox.properties import List, String
1547+from checkbox.variables import get_variables
1548+
1549+
1550+class ComponentSection:
1551+ """
1552+ Component section which is essentially a container of modules. These
1553+ map to the modules referenced in the configuration passed as argument
1554+ to the constructor.
1555+ """
1556+
1557+ modules = List(type=String())
1558+ whitelist = List(type=String(), default_factory=lambda:"")
1559+ blacklist = List(type=String(), default_factory=lambda:"")
1560+
1561+ def __init__(self, config, name):
1562+ """
1563+ Constructor which takes a configuration instance and name as
1564+ argument. The former is expected to contain modules.
1565+ """
1566+ self._config = config
1567+ self.name = name
1568+
1569+ self.modules = config.modules
1570+ self.whitelist = config.get("whitelist")
1571+ self.blacklist = config.get("blacklist")
1572+
1573+ @cache
1574+ def get_names(self):
1575+ """
1576+ Get all the module names contained in the filenames or directories
1577+ for this section.
1578+ """
1579+ whitelist_patterns = [re.compile(r"^%s$" % r) for r in self.whitelist]
1580+ blacklist_patterns = [re.compile(r"^%s$" % r) for r in self.blacklist]
1581+
1582+ # Determine names
1583+ names = set()
1584+ filenames = itertools.chain(*[path_expand_recursive(m)
1585+ for m in self.modules])
1586+ for filename in filenames:
1587+ name = posixpath.basename(filename)
1588+ if not name.endswith(".py") or name == "__init__.py":
1589+ # Ignore silently
1590+ continue
1591+
1592+ name = name.replace(".py", "")
1593+ if whitelist_patterns:
1594+ if not [name for p in whitelist_patterns if p.match(name)]:
1595+ logging.info("Not whitelisted module: %s", name)
1596+ continue
1597+ elif blacklist_patterns:
1598+ if [name for p in blacklist_patterns if p.match(name)]:
1599+ logging.info("Blacklisted module: %s", name)
1600+ continue
1601+
1602+ names.add(name)
1603+
1604+ return list(names)
1605+
1606+ def has_module(self, name):
1607+ """
1608+ Check if the given name is in this section.
1609+ """
1610+ return name in self.get_names()
1611+
1612+ def load_module(self, name):
1613+ """
1614+ Load a single module by name from this section.
1615+ """
1616+ logging.info("Loading module %s from section %s",
1617+ name, self.name)
1618+
1619+ if not self.has_module(name):
1620+ raise Exception("No such such module: %s" % name)
1621+
1622+ filenames = itertools.chain(*[path_expand_recursive(m)
1623+ for m in self.modules])
1624+ for filename in filenames:
1625+ if filename.endswith(".py") and posixpath.exists(filename):
1626+ basename = posixpath.basename(filename)
1627+ basename = basename.replace(".py", "")
1628+ if basename == name:
1629+ globals = {}
1630+ exec(open(filename).read(), globals)
1631+ if "factory" not in globals:
1632+ raise Exception("Variable 'factory' not found in: %s" \
1633+ % filename)
1634+
1635+ module = globals["factory"]()
1636+ module.__module__ = name
1637+
1638+ config_name = "/".join([self.name, name])
1639+ config = self._config.parent.get_section(config_name)
1640+
1641+ # Set configuration values
1642+ variables = get_variables(module)
1643+ environ = dict([(k.lower(), v) for k, v in os.environ.items()])
1644+ for attribute, variable in variables.items():
1645+ if config and attribute.name in config:
1646+ value = config.get(attribute.name)
1647+ variable.set(value)
1648+ else:
1649+ value = variable.get()
1650+ if isinstance(value, str):
1651+ value = value % environ
1652+ variable.set(value)
1653+ elif isinstance(value, list):
1654+ value = [v % environ for v in value]
1655+ variable.set(value)
1656+
1657+ # Check required attributes
1658+ for attribute, variable in variables.items():
1659+ value = variable.get()
1660+ if value is None and variable._required:
1661+ raise Exception("Configuration '%s' missing " \
1662+ "required attribute: %s" \
1663+ % (config_name, attribute.name))
1664+
1665+ return module
1666+
1667+ raise Exception("Failed to find module '%s' in: %s" % (name, filenames))
1668+
1669+ def load_modules(self):
1670+ """
1671+ Load all modules contained in this section.
1672+ """
1673+ modules = []
1674+ for name in self.get_names():
1675+ module = self.load_module(name)
1676+ modules.append(module)
1677+
1678+ return modules
1679+
1680+
1681+class ComponentManager:
1682+ """
1683+ Component manager which is essentially a container of sections.
1684+ """
1685+
1686+ _section_factory = ComponentSection
1687+
1688+ def __init__(self, config):
1689+ """
1690+ Constructor which takes a configuration instance as argument. This
1691+ will be used to load sections by name.
1692+ """
1693+ self._config = config
1694+
1695+ def load_section(self, name):
1696+ """
1697+ Load a section by name which must correspond to a module in the
1698+ configuration instance pased as argument to the constructor.
1699+ """
1700+ logging.info("Loading component section %s", name)
1701+ config = self._config.get_section(name)
1702+ return self._section_factory(config, name)
1703
1704=== added directory 'checkbox/contrib'
1705=== added file 'checkbox/contrib/REThread.py'
1706--- checkbox/contrib/REThread.py 1970-01-01 00:00:00 +0000
1707+++ checkbox/contrib/REThread.py 2013-10-01 18:38:21 +0000
1708@@ -0,0 +1,142 @@
1709+'''Enhanced threading.Thread which can deliver a return value and propagate
1710+exceptions from the called thread to the calling thread.
1711+
1712+Copyright (C) 2007 Canonical Ltd.
1713+Author: Martin Pitt <martin.pitt@ubuntu.com>
1714+
1715+This program is free software; you can redistribute it and/or modify it
1716+under the terms of the GNU General Public License as published by the
1717+Free Software Foundation; either version 2 of the License, or (at your
1718+option) any later version. See http://www.gnu.org/copyleft/gpl.html for
1719+the full text of the license.
1720+'''
1721+
1722+import threading, sys
1723+
1724+class REThread(threading.Thread):
1725+ '''Enhanced threading.Thread which can deliver a return value and propagate
1726+ exceptions from the called thread to the calling thread.'''
1727+
1728+ def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
1729+ verbose=None):
1730+ '''Initialize Thread, identical to threading.Thread.__init__().'''
1731+ #Note that due to api differences in python (verbose disappeared
1732+ #at some point before 3.3 and got replaced by daemon), we just
1733+ #ignore the verbose attribute. It's not used anywhere in checkbox
1734+ #so this is safe for our purposes.
1735+ self.__target = target
1736+ self.__args = args
1737+ self.__kwargs = kwargs
1738+ self._retval = None
1739+ self._exception = None
1740+
1741+ threading.Thread.__init__(self)
1742+
1743+ def run(self):
1744+ '''Run target function, identical to threading.Thread.run().'''
1745+
1746+ if self.__target:
1747+ try:
1748+ self._retval = self.__target(*self.__args, **self.__kwargs)
1749+ except:
1750+ self._exception = sys.exc_info()
1751+
1752+ def return_value(self):
1753+ '''Return value from target function.
1754+
1755+ This can only be called after the thread has finished, i. e. when
1756+ isAlive() is False and did not terminate with an exception.'''
1757+
1758+ assert not self.isAlive()
1759+ assert not self._exception
1760+ return self._retval
1761+
1762+ def exc_info(self):
1763+ '''Return a tuple (type, value, traceback) of the exception caught in
1764+ run().'''
1765+
1766+ return self._exception
1767+
1768+ def exc_raise(self):
1769+ '''Raises the exception caught in the thread.
1770+
1771+ Does nothing if no exception was caught.'''
1772+
1773+ if self._exception:
1774+ raise self._exception[0](self._exception[1]).with_traceback(self._exception[2])
1775+
1776+#
1777+# Unit test
1778+#
1779+
1780+if __name__ == '__main__':
1781+ import unittest, time, traceback, exceptions
1782+
1783+ def idle(seconds):
1784+ '''Test thread to just wait a bit.'''
1785+
1786+ time.sleep(seconds)
1787+
1788+ def div(x, y):
1789+ '''Test thread to divide two numbers.'''
1790+
1791+ return x / y
1792+
1793+ class _REThreadTest(unittest.TestCase):
1794+ def test_return_value(self):
1795+ '''Test that return value works properly.'''
1796+
1797+ t = REThread(target=div, args=(42, 2))
1798+ t.start()
1799+ t.join()
1800+ # exc_raise() should be a no-op on successful functions
1801+ t.exc_raise()
1802+ self.assertEqual(t.return_value(), 21)
1803+ self.assertEqual(t.exc_info(), None)
1804+
1805+ def test_no_return_value(self):
1806+ '''Test that REThread works if run() does not return anything.'''
1807+
1808+ t = REThread(target=idle, args=(0.5,))
1809+ t.start()
1810+ # thread must be joined first
1811+ self.assertRaises(AssertionError, t.return_value)
1812+ t.join()
1813+ self.assertEqual(t.return_value(), None)
1814+ self.assertEqual(t.exc_info(), None)
1815+
1816+ def test_exception(self):
1817+ '''Test that exception in thread is caught and passed.'''
1818+
1819+ t = REThread(target=div, args=(1, 0))
1820+ t.start()
1821+ t.join()
1822+ # thread did not terminate normally, no return value
1823+ self.assertRaises(AssertionError, t.return_value)
1824+ self.assert_(t.exc_info()[0] == exceptions.ZeroDivisionError)
1825+ exc = traceback.format_exception(t.exc_info()[0], t.exc_info()[1],
1826+ t.exc_info()[2])
1827+ self.assert_(exc[-1].startswith('ZeroDivisionError'))
1828+ self.assert_(exc[-2].endswith('return x / y\n'))
1829+
1830+ def test_exc_raise(self):
1831+ '''Test that exc_raise() raises caught thread exception.'''
1832+
1833+ t = REThread(target=div, args=(1, 0))
1834+ t.start()
1835+ t.join()
1836+ # thread did not terminate normally, no return value
1837+ self.assertRaises(AssertionError, t.return_value)
1838+ raised = False
1839+ try:
1840+ t.exc_raise()
1841+ except:
1842+ raised = True
1843+ e = sys.exc_info()
1844+ exc = traceback.format_exception(e[0], e[1], e[2])
1845+ self.assert_(exc[-1].startswith('ZeroDivisionError'))
1846+ self.assert_(exc[-2].endswith('return x / y\n'))
1847+ self.assert_(raised)
1848+
1849+ unittest.main()
1850+
1851
1852=== added file 'checkbox/contrib/__init__.py'
1853=== added file 'checkbox/contrib/bpickle.py'
1854--- checkbox/contrib/bpickle.py 1970-01-01 00:00:00 +0000
1855+++ checkbox/contrib/bpickle.py 2013-10-01 18:38:21 +0000
1856@@ -0,0 +1,188 @@
1857+"""
1858+Copyright (c) 2006, Gustavo Niemeyer <gustavo@niemeyer.net>
1859+
1860+All rights reserved.
1861+
1862+Redistribution and use in source and binary forms, with or without
1863+modification, are permitted provided that the following conditions are met:
1864+
1865+ * Redistributions of source code must retain the above copyright notice,
1866+ this list of conditions and the following disclaimer.
1867+ * Redistributions in binary form must reproduce the above copyright notice,
1868+ this list of conditions and the following disclaimer in the documentation
1869+ and/or other materials provided with the distribution.
1870+ * Neither the name of the copyright holder nor the names of its
1871+ contributors may be used to endorse or promote products derived from
1872+ this software without specific prior written permission.
1873+
1874+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1875+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1876+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1877+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
1878+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
1879+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1880+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1881+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
1882+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
1883+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1884+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1885+"""
1886+
1887+
1888+dumps_table = {}
1889+loads_table = {}
1890+
1891+
1892+def dumps(obj, _dt=None):
1893+ if not _dt:
1894+ _dt = dumps_table
1895+
1896+ type_names = [type(obj)]
1897+ for type_name in type_names:
1898+ if type_name in _dt:
1899+ return _dt[type_name](obj)
1900+
1901+ type_names.extend(type_name.__bases__)
1902+
1903+ raise ValueError("Unsupported type: %s" % type(obj))
1904+
1905+
1906+def loads(str, _lt=loads_table):
1907+ if not str:
1908+ raise ValueError("Can't load empty string")
1909+ try:
1910+ return _lt[str[0]](str, 0)[0]
1911+ except KeyError as e:
1912+ raise ValueError("Unknown type character: %s" % e)
1913+ except IndexError:
1914+ raise ValueError("Corrupted data")
1915+
1916+
1917+def dumps_bool(obj):
1918+ return ("b%d" % int(obj)).encode("ascii")
1919+
1920+
1921+def dumps_int(obj):
1922+ return ("i%s;" % obj).encode("ascii")
1923+
1924+
1925+def dumps_float(obj):
1926+ return ("f%r;" % obj).encode("ascii")
1927+
1928+
1929+def dumps_bytes(obj):
1930+ return ("c%s:" % len(obj)).encode("ascii") + obj
1931+
1932+
1933+def dumps_str(obj):
1934+ obj = obj.encode("utf-8")
1935+ return ("s%s:" % len(obj)).encode("ascii") + obj
1936+
1937+
1938+def dumps_list(obj, _dt=None):
1939+ return b"l" + b"".join([dumps(val, _dt) for val in obj]) + b";"
1940+
1941+
1942+def dumps_tuple(obj, _dt=None):
1943+ return b"t" + b"".join([dumps(val, _dt) for val in obj]) + b";"
1944+
1945+
1946+def dumps_dict(obj, _dt=None):
1947+ res = []
1948+ keys = sorted(obj.keys())
1949+ append = res.append
1950+ for key in keys:
1951+ val = obj[key]
1952+ append(dumps(key, _dt))
1953+ append(dumps(val, _dt))
1954+ return b"d" + b"".join(res) + b";"
1955+
1956+
1957+def dumps_none(obj):
1958+ return b"n"
1959+
1960+
1961+def loads_bool(str, pos):
1962+ return bool(int(chr(str[pos + 1]))), pos + 2
1963+
1964+
1965+def loads_int(str, pos):
1966+ endpos = str.index(b";", pos)
1967+ return int(str[pos + 1:endpos]), endpos + 1
1968+
1969+
1970+def loads_float(str, pos):
1971+ endpos = str.index(b";", pos)
1972+ return float(str[pos + 1:endpos]), endpos + 1
1973+
1974+
1975+def loads_bytes(str, pos):
1976+ startpos = str.index(b":", pos) + 1
1977+ endpos = startpos + int(str[pos + 1:startpos - 1])
1978+ return str[startpos:endpos], endpos
1979+
1980+
1981+def loads_str(str, pos):
1982+ startpos = str.index(b":", pos) + 1
1983+ endpos = startpos + int(str[pos + 1:startpos - 1])
1984+ return str[startpos:endpos].decode("utf-8"), endpos
1985+
1986+
1987+def loads_list(str, pos, _lt=loads_table):
1988+ pos += 1
1989+ res = []
1990+ append = res.append
1991+ while str[pos] != ord(";"):
1992+ obj, pos = _lt[str[pos]](str, pos)
1993+ append(obj)
1994+ return res, pos + 1
1995+
1996+
1997+def loads_tuple(str, pos, _lt=loads_table):
1998+ pos += 1
1999+ res = []
2000+ append = res.append
2001+ while str[pos] != ord(";"):
2002+ obj, pos = _lt[str[pos]](str, pos)
2003+ append(obj)
2004+ return tuple(res), pos + 1
2005+
2006+
2007+def loads_dict(str, pos, _lt=loads_table):
2008+ pos += 1
2009+ res = {}
2010+ while str[pos] != ord(";"):
2011+ key, pos = _lt[str[pos]](str, pos)
2012+ val, pos = _lt[str[pos]](str, pos)
2013+ res[key] = val
2014+ return res, pos + 1
2015+
2016+
2017+def loads_none(str, pos):
2018+ return None, pos + 1
2019+
2020+
2021+dumps_table.update({
2022+ bool: dumps_bool,
2023+ int: dumps_int,
2024+ float: dumps_float,
2025+ bytes: dumps_bytes,
2026+ str: dumps_str,
2027+ list: dumps_list,
2028+ tuple: dumps_tuple,
2029+ dict: dumps_dict,
2030+ type(None): dumps_none,
2031+ })
2032+
2033+loads_table.update({
2034+ ord("b"): loads_bool,
2035+ ord("i"): loads_int,
2036+ ord("f"): loads_float,
2037+ ord("c"): loads_str,
2038+ ord("s"): loads_str,
2039+ ord("u"): loads_str,
2040+ ord("l"): loads_list,
2041+ ord("t"): loads_tuple,
2042+ ord("d"): loads_dict,
2043+ ord("n"): loads_none,
2044+ })
2045
2046=== added file 'checkbox/contrib/gdk.py'
2047--- checkbox/contrib/gdk.py 1970-01-01 00:00:00 +0000
2048+++ checkbox/contrib/gdk.py 2013-10-01 18:38:21 +0000
2049@@ -0,0 +1,46 @@
2050+#!/usr/bin/python3
2051+# -*- coding: utf-8 -*-
2052+#
2053+# Python-XRandR provides a high level API for the XRandR extension of the
2054+# X.org server. XRandR allows to configure resolution, refresh rate, rotation
2055+# of the screen and multiple outputs of graphics cards.
2056+#
2057+# This module allows to get information for gtk.gdk.Screen objects
2058+#
2059+# In many aspects it follows the design of the xrand tool written by
2060+# Keith Packard.
2061+#
2062+# Copyright 2007 © Sebastian Heinlein <sebastian.heinlein@web.de>
2063+#
2064+# This library is free software; you can redistribute it and/or
2065+# modify it under the terms of the GNU Lesser General Public
2066+# License as published by the Free Software Foundation; either
2067+# version 2.1 of the License, or any later version.
2068+#
2069+# This library is distributed in the hope that it will be useful,
2070+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2071+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2072+# Lesser General Public License for more details.
2073+#
2074+# You should have received a copy of the GNU Lesser General Public
2075+# License along with this library; if not, write to the Free Software
2076+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2077+
2078+from gi.repository import Gdk
2079+from checkbox.contrib import xrandr
2080+
2081+#FIXME: Perhaps using gdk_x11_screen_get_xscreen would be more elegant
2082+
2083+def get_default_screen_config():
2084+ dpy = Gdk.Display.get_default()
2085+ screen = Gdk.Display.get_default_screen(dpy)
2086+ return get_screen_config(screen)
2087+
2088+def get_screen_config(screen):
2089+ """Returns the XRandR screen config instance for the given gtk.gdk.Screen"""
2090+ dpy = screen.get_display()
2091+ dpy_url = dpy.get_name()
2092+ count = screen.get_number()
2093+ return xrandr.get_screen_of_display(dpy_url, count)
2094+
2095+# vim:ts=4:sw=4:et
2096
2097=== added file 'checkbox/contrib/glock.py'
2098--- checkbox/contrib/glock.py 1970-01-01 00:00:00 +0000
2099+++ checkbox/contrib/glock.py 2013-10-01 18:38:21 +0000
2100@@ -0,0 +1,311 @@
2101+#!/usr/bin/env python3
2102+# -*- coding: latin1 -*-
2103+#----------------------------------------------------------------------------
2104+# glock.py: Global mutex
2105+#
2106+# See __doc__ string below.
2107+#
2108+# Requires:
2109+# - Python 1.5.2 or newer (www.python.org)
2110+# - On windows: win32 extensions installed
2111+# (http://www.python.org/windows/win32all/win32all.exe)
2112+# - OS: Unix, Windows.
2113+#
2114+# $Id: //depot/rgutils/rgutils/glock.py#5 $
2115+#----------------------------------------------------------------------------
2116+'''
2117+This module defines the class GlobalLock that implements a global
2118+(inter-process) mutex on Windows and Unix, using file-locking on
2119+Unix.
2120+
2121+@see: class L{GlobalLock} for more details.
2122+'''
2123+__version__ = '0.2.' + '$Revision: #5 $'[12:-2]
2124+__author__ = 'Richard Gruet', 'rjgruet@yahoo.com'
2125+__date__ = '$Date: 2005/06/19 $'[7:-2], '$Author: rgruet $'[9:-2]
2126+__since__ = '2000-01-22'
2127+__doc__ += '\n@author: %s (U{%s})\n@version: %s' % (__author__[0],
2128+ __author__[1], __version__)
2129+__all__ = ['GlobalLock', 'GlobalLockError', 'LockAlreadyAcquired', 'NotOwner']
2130+
2131+# Imports:
2132+import sys, string, os, errno, re, posixpath
2133+
2134+# System-dependent imports for locking implementation:
2135+_windows = (sys.platform == 'win32')
2136+
2137+if _windows:
2138+ try:
2139+ import win32event, win32api, pywintypes
2140+ except ImportError:
2141+ sys.stderr.write('The win32 extensions need to be installed!')
2142+ try:
2143+ import ctypes
2144+ except ImportError:
2145+ ctypes = None
2146+else: # assume Unix
2147+ try:
2148+ import fcntl
2149+ except ImportError:
2150+ sys.stderr.write("On what kind of OS am I ? (Mac?) I should be on "
2151+ "Unix but can't import fcntl.\n")
2152+ raise
2153+ import threading
2154+
2155+# Exceptions :
2156+# ----------
2157+class GlobalLockError(Exception):
2158+ ''' Error raised by the glock module.
2159+ '''
2160+ pass
2161+
2162+class NotOwner(GlobalLockError):
2163+ ''' Attempt to release somebody else's lock.
2164+ '''
2165+ pass
2166+
2167+class LockAlreadyAcquired(GlobalLockError):
2168+ ''' Non-blocking acquire but lock already seized.
2169+ '''
2170+ pass
2171+
2172+
2173+#----------------------------------------------------------------------------
2174+class GlobalLock:
2175+#----------------------------------------------------------------------------
2176+ ''' A global mutex.
2177+
2178+ B{Specification}
2179+
2180+ - The lock must act as a global mutex, ie block between different
2181+ candidate processus, but ALSO between different candidate
2182+ threads of the same process.
2183+
2184+ - It must NOT block in case of reentrant lock request issued by
2185+ the SAME thread.
2186+ - Extraneous unlocks should be ideally harmless.
2187+
2188+ B{Implementation}
2189+
2190+ In Python there is no portable global lock AFAIK. There is only a
2191+ LOCAL/ in-process Lock mechanism (threading.RLock), so we have to
2192+ implement our own solution:
2193+
2194+ - Unix: use fcntl.flock(). Recursive calls OK. Different process OK.
2195+ But <> threads, same process don't block so we have to use an extra
2196+ threading.RLock to fix that point.
2197+ - Windows: We use WIN32 mutex from Python Win32 extensions. Can't use
2198+ std module msvcrt.locking(), because global lock is OK, but
2199+ blocks also for 2 calls from the same thread!
2200+ '''
2201+ RE_ERROR_MSG = re.compile ("^\[Errno ([0-9]+)\]")
2202+
2203+ def __init__(self, fpath, lockInitially=False, logger=None):
2204+ ''' Creates (or opens) a global lock.
2205+
2206+ @param fpath: Path of the file used as lock target. This is also
2207+ the global id of the lock. The file will be created
2208+ if non existent.
2209+ @param lockInitially: if True locks initially.
2210+ @param logger: an optional logger object.
2211+ '''
2212+ self.logger = logger
2213+ self.fpath = fpath
2214+ if posixpath.exists(fpath):
2215+ self.previous_lockfile_present = True
2216+ else:
2217+ self.previous_lockfile_present = False
2218+ if _windows:
2219+ self.name = string.replace(fpath, '\\', '_')
2220+ self.mutex = win32event.CreateMutex(None, lockInitially, self.name)
2221+ else: # Unix
2222+ self.name = fpath
2223+ self.flock = open(fpath, 'w')
2224+ self.fdlock = self.flock.fileno()
2225+ self.threadLock = threading.RLock()
2226+ if lockInitially:
2227+ self.acquire()
2228+
2229+ def __del__(self):
2230+ #print '__del__ called' ##
2231+ try: self.release()
2232+ except: pass
2233+ if _windows:
2234+ win32api.CloseHandle(self.mutex)
2235+ else:
2236+ try: self.flock.close()
2237+ except: pass
2238+
2239+ def __repr__(self):
2240+ return '<Global lock @ %s>' % self.name
2241+
2242+ def acquire(self, blocking=False):
2243+ """ Locks. Attemps to acquire a lock.
2244+
2245+ @param blocking: If True, suspends caller until done. Otherwise,
2246+ LockAlreadyAcquired is raised if the lock cannot be acquired immediately.
2247+
2248+ On windows an IOError is always raised after ~10 sec if the lock
2249+ can't be acquired.
2250+ @exception GlobalLockError: if lock can't be acquired (timeout)
2251+ @exception LockAlreadyAcquired: someone already has the lock and
2252+ the caller decided not to block.
2253+ """
2254+ if self.logger:
2255+ self.logger.info('creating lockfile')
2256+ if _windows:
2257+ if blocking:
2258+ timeout = win32event.INFINITE
2259+ else:
2260+ timeout = 0
2261+ r = win32event.WaitForSingleObject(self.mutex, timeout)
2262+ if r == win32event.WAIT_FAILED:
2263+ raise GlobalLockError("Can't acquire mutex: error")
2264+ if not blocking and r == win32event.WAIT_TIMEOUT:
2265+ raise LockAlreadyAcquired('Lock %s already acquired by '
2266+ 'someone else' % self.name)
2267+ else:
2268+ # First, acquire the global (inter-process) lock:
2269+ if blocking:
2270+ options = fcntl.LOCK_EX
2271+ else:
2272+ options = fcntl.LOCK_EX|fcntl.LOCK_NB
2273+ try:
2274+ fcntl.flock(self.fdlock, options)
2275+ except IOError as message: #(errno 13: perm. denied,
2276+ # 36: Resource deadlock avoided)
2277+ if not blocking and self._errnoOf (message) == errno.EWOULDBLOCK:
2278+ raise LockAlreadyAcquired('Lock %s already acquired by '
2279+ 'someone else' % self.name)
2280+ else:
2281+ raise GlobalLockError('Cannot acquire lock on "file" '
2282+ '%s: %s\n' % (self.name, message))
2283+ #print 'got file lock.' ##
2284+
2285+ # Then acquire the local (inter-thread) lock:
2286+ if not self.threadLock.acquire(blocking):
2287+ fcntl.flock(self.fdlock, fcntl.LOCK_UN) # release global lock
2288+ raise LockAlreadyAcquired('Lock %s already acquired by '
2289+ 'someone else' % self.name)
2290+ if self.previous_lockfile_present and self.logger:
2291+ self.logger.warn("Stale lockfile detected and claimed.")
2292+ #print 'got thread lock.' ##
2293+
2294+ self.is_locked = True
2295+
2296+ def release(self, skip_delete=False):
2297+ ''' Unlocks. (caller must own the lock!)
2298+
2299+ @param skip_delete: don't try to delete the file. This can
2300+ be used when the original filename has changed; for
2301+ instance, if the lockfile is erased out-of-band, or if
2302+ the directory it contains has been renamed.
2303+
2304+ @return: The lock count.
2305+ @exception IOError: if file lock can't be released
2306+ @exception NotOwner: Attempt to release somebody else's lock.
2307+ '''
2308+ if not self.is_locked:
2309+ return
2310+ if not skip_delete:
2311+ if self.logger:
2312+ self.logger.debug('Removing lock file: %s', self.fpath)
2313+ os.unlink(self.fpath)
2314+ elif self.logger:
2315+ # At certain times the lockfile will have been removed or
2316+ # moved away before we call release(); log a message because
2317+ # this is unusual and could be an error.
2318+ self.logger.debug('Oops, my lock file disappeared: %s', self.fpath)
2319+ if _windows:
2320+ if ctypes:
2321+ result = ctypes.windll.kernel32.ReleaseMutex(self.mutex.handle)
2322+ if not result:
2323+ raise NotOwner("Attempt to release somebody else's lock")
2324+ else:
2325+ try:
2326+ win32event.ReleaseMutex(self.mutex)
2327+ #print "released mutex"
2328+ except pywintypes.error as e:
2329+ errCode, fctName, errMsg = e.args
2330+ if errCode == 288:
2331+ raise NotOwner("Attempt to release somebody else's lock")
2332+ else:
2333+ raise GlobalLockError('%s: err#%d: %s' % (fctName, errCode,
2334+ errMsg))
2335+ else:
2336+ # First release the local (inter-thread) lock:
2337+ try:
2338+ self.threadLock.release()
2339+ except AssertionError:
2340+ raise NotOwner("Attempt to release somebody else's lock")
2341+
2342+ # Then release the global (inter-process) lock:
2343+ try:
2344+ fcntl.flock(self.fdlock, fcntl.LOCK_UN)
2345+ except IOError: # (errno 13: permission denied)
2346+ raise GlobalLockError('Unlock of file "%s" failed\n' %
2347+ self.name)
2348+ self.is_locked = False
2349+
2350+ def _errnoOf (self, message):
2351+ match = self.RE_ERROR_MSG.search(str(message))
2352+ if match:
2353+ return int(match.group(1))
2354+ else:
2355+ raise Exception ('Malformed error message "%s"' % message)
2356+
2357+#----------------------------------------------------------------------------
2358+def test():
2359+#----------------------------------------------------------------------------
2360+ ##TODO: a more serious test with distinct processes !
2361+
2362+ print('Testing glock.py...')
2363+
2364+ # unfortunately can't test inter-process lock here!
2365+ lockName = 'myFirstLock'
2366+ l = GlobalLock(lockName)
2367+ if not _windows:
2368+ assert posixpath.exists(lockName)
2369+ l.acquire()
2370+ l.acquire() # reentrant lock, must not block
2371+ l.release()
2372+ l.release()
2373+
2374+ try: l.release()
2375+ except NotOwner: pass
2376+ else: raise Exception('should have raised a NotOwner exception')
2377+
2378+ # Check that <> threads of same process do block:
2379+ import threading, time
2380+ thread = threading.Thread(target=threadMain, args=(l,))
2381+ print('main: locking...', end=' ')
2382+ l.acquire()
2383+ print(' done.')
2384+ thread.start()
2385+ time.sleep(3)
2386+ print('\nmain: unlocking...', end=' ')
2387+ l.release()
2388+ print(' done.')
2389+ time.sleep(0.1)
2390+
2391+ print('=> Test of glock.py passed.')
2392+ return l
2393+
2394+def threadMain(lock):
2395+ print('thread started(%s).' % lock)
2396+ try: lock.acquire(blocking=False)
2397+ except LockAlreadyAcquired: pass
2398+ else: raise Exception('should have raised LockAlreadyAcquired')
2399+ print('thread: locking (should stay blocked for ~ 3 sec)...', end=' ')
2400+ lock.acquire()
2401+ print('thread: locking done.')
2402+ print('thread: unlocking...', end=' ')
2403+ lock.release()
2404+ print(' done.')
2405+ print('thread ended.')
2406+
2407+#----------------------------------------------------------------------------
2408+# M A I N
2409+#----------------------------------------------------------------------------
2410+if __name__ == "__main__":
2411+ l = test()
2412
2413=== added file 'checkbox/contrib/persist.py'
2414--- checkbox/contrib/persist.py 1970-01-01 00:00:00 +0000
2415+++ checkbox/contrib/persist.py 2013-10-01 18:38:21 +0000
2416@@ -0,0 +1,552 @@
2417+# Copyright (c) 2006 Canonical
2418+# Copyright (c) 2004 Conectiva, Inc.
2419+#
2420+# Written by Gustavo Niemeyer <gustavo@niemeyer.net>
2421+#
2422+# This Python module is free software; you can redistribute it and/or
2423+# modify it under the terms of the GNU General Public License as published
2424+# by the Free Software Foundation; either version 2 of the License, or (at
2425+# your option) any later version.
2426+#
2427+# This Python module is distributed in the hope that it will be useful,
2428+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2429+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2430+# General Public License for more details.
2431+#
2432+# You should have received a copy of the GNU General Public License
2433+# along with this Python module; if not, write to the Free Software
2434+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2435+#
2436+import os
2437+import sys
2438+import copy
2439+import re
2440+import posixpath
2441+
2442+from checkbox.lib.safe import safe_close
2443+
2444+__all__ = ["Persist", "MemoryBackend", "PickleBackend", "BPickleBackend",
2445+ "path_string_to_tuple", "path_tuple_to_string", "RootedPersist",
2446+ "PersistError", "PersistReadOnlyError"]
2447+
2448+
2449+NOTHING = object()
2450+
2451+
2452+class PersistError(Exception):
2453+ pass
2454+
2455+
2456+class PersistReadOnlyError(PersistError):
2457+ pass
2458+
2459+
2460+class Persist:
2461+ """Persistence handler.
2462+
2463+ There are three different kinds of opition maps, regarding the
2464+ persistence and priority that maps are queried.
2465+
2466+ hard - Options are persistent.
2467+ soft - Options are not persistent, and have a higher priority
2468+ than persistent options.
2469+ weak - Options are not persistent, and have a lower priority
2470+ than persistent options.
2471+
2472+ @ivar filename: The name of the file where persist data is saved
2473+ or None if not filename is available.
2474+ """
2475+
2476+
2477+ def __init__(self, filename=None, backend=None):
2478+ """
2479+ @param backend: The backend to use. If none is specified,
2480+ L{BPickleBackend} will be used.
2481+ @param filename: The default filename to save to and load from. If
2482+ specified, and the file exists, it will be immediately
2483+ loaded. Specifying this will also allow L{save} to be called
2484+ without any arguments to save the persist.
2485+ """
2486+ if backend is None:
2487+ backend = BPickleBackend()
2488+ self._backend = backend
2489+ self._hardmap = backend.new()
2490+ self._softmap = {}
2491+ self._weakmap = {}
2492+ self._readonly = False
2493+ self._modified = False
2494+ self.filename = filename
2495+ if filename is not None and posixpath.exists(filename):
2496+ self.load(filename)
2497+
2498+ def _get_readonly(self):
2499+ return self._readonly
2500+
2501+ def _set_readonly(self, flag):
2502+ self._readonly = bool(flag)
2503+
2504+ def _get_modified(self):
2505+ return self._modified
2506+
2507+ readonly = property(_get_readonly, _set_readonly)
2508+ modified = property(_get_modified)
2509+
2510+ def reset_modified(self):
2511+ self._modified = False
2512+
2513+ def assert_writable(self):
2514+ if self._readonly:
2515+ raise PersistReadOnlyError("Configuration is in readonly mode.")
2516+
2517+ def load(self, filepath):
2518+ filepath = posixpath.expanduser(filepath)
2519+ if not posixpath.isfile(filepath):
2520+ raise PersistError("File not found: %s" % filepath)
2521+ if posixpath.getsize(filepath) == 0:
2522+ return
2523+ try:
2524+ self._hardmap = self._backend.load(filepath)
2525+ except:
2526+ filepathold = filepath+".old"
2527+ if (posixpath.isfile(filepathold) and
2528+ posixpath.getsize(filepathold) > 0):
2529+ # warning("Broken configuration file at %s" % filepath)
2530+ # warning("Trying backup at %s" % filepathold)
2531+ try:
2532+ self._hardmap = self._backend.load(filepathold)
2533+ except:
2534+ raise PersistError("Broken configuration file at %s" %
2535+ filepathold)
2536+ else:
2537+ raise PersistError("Broken configuration file at %s" %
2538+ filepath)
2539+
2540+ def save(self, filepath=None):
2541+ """Save the persist to the given C{filepath}.
2542+
2543+ If None is specified, then the filename passed during construction will
2544+ be used.
2545+ """
2546+ if filepath is None:
2547+ if self.filename is None:
2548+ return
2549+ filepath = self.filename
2550+ filepath = posixpath.expanduser(filepath)
2551+ if posixpath.isfile(filepath):
2552+ os.rename(filepath, filepath+".old")
2553+ dirname = posixpath.dirname(filepath)
2554+ if dirname and not posixpath.isdir(dirname):
2555+ os.makedirs(dirname)
2556+ self._backend.save(filepath, self._hardmap)
2557+
2558+ def _traverse(self, obj, path, default=NOTHING, setvalue=NOTHING):
2559+ if setvalue is not NOTHING:
2560+ setvalue = self._backend.copy(setvalue)
2561+ queue = list(path)
2562+ marker = NOTHING
2563+ newobj = obj
2564+ while queue:
2565+ obj = newobj
2566+ elem = queue.pop(0)
2567+ newobj = self._backend.get(obj, elem)
2568+ if newobj is NotImplemented:
2569+ if queue:
2570+ path = path[:-len(queue)]
2571+ raise PersistError("Can't traverse %r (%r): %r" %
2572+ (type(obj), path_tuple_to_string(path),
2573+ str(obj)))
2574+ if newobj is marker:
2575+ break
2576+ if newobj is not marker:
2577+ if setvalue is not marker:
2578+ newobj = self._backend.set(obj, elem, setvalue)
2579+ else:
2580+ if setvalue is marker:
2581+ newobj = default
2582+ else:
2583+ while True:
2584+ if len(queue) > 0:
2585+ if type(queue[0]) is int:
2586+ newvalue = []
2587+ else:
2588+ newvalue = {}
2589+ else:
2590+ newvalue = setvalue
2591+ newobj = self._backend.set(obj, elem, newvalue)
2592+ if newobj is NotImplemented:
2593+ raise PersistError("Can't traverse %r with %r" %
2594+ (type(obj), type(elem)))
2595+ if not queue:
2596+ break
2597+ obj = newobj
2598+ elem = queue.pop(0)
2599+ return newobj
2600+
2601+ def _getvalue(self, path, soft=False, hard=False, weak=False):
2602+ if type(path) is str:
2603+ path = path_string_to_tuple(path)
2604+ marker = NOTHING
2605+ if soft:
2606+ value = self._traverse(self._softmap, path, marker)
2607+ elif hard:
2608+ value = self._traverse(self._hardmap, path, marker)
2609+ elif weak:
2610+ value = self._traverse(self._weakmap, path, marker)
2611+ else:
2612+ value = self._traverse(self._softmap, path, marker)
2613+ if value is marker:
2614+ value = self._traverse(self._hardmap, path, marker)
2615+ if value is marker:
2616+ value = self._traverse(self._weakmap, path, marker)
2617+ return value
2618+
2619+ def has(self, path, value=NOTHING, soft=False, hard=False, weak=False):
2620+ obj = self._getvalue(path, soft, hard, weak)
2621+ marker = NOTHING
2622+ if obj is marker:
2623+ return False
2624+ elif value is marker:
2625+ return True
2626+ result = self._backend.has(obj, value)
2627+ if result is NotImplemented:
2628+ raise PersistError("Can't check %r for containment" % type(obj))
2629+ return result
2630+
2631+ def keys(self, path, soft=False, hard=False, weak=False):
2632+ obj = self._getvalue(path, soft, hard, weak)
2633+ if obj is NOTHING:
2634+ return []
2635+ result = self._backend.keys(obj)
2636+ if result is NotImplemented:
2637+ raise PersistError("Can't return keys for %s" % type(obj))
2638+ return result
2639+
2640+ def get(self, path, default=None, soft=False, hard=False, weak=False):
2641+ value = self._getvalue(path, soft, hard, weak)
2642+ if value is NOTHING:
2643+ return default
2644+ return self._backend.copy(value)
2645+
2646+ def set(self, path, value, soft=False, weak=False):
2647+ assert path
2648+ if type(path) is str:
2649+ path = path_string_to_tuple(path)
2650+ if soft:
2651+ map = self._softmap
2652+ elif weak:
2653+ map = self._weakmap
2654+ else:
2655+ self.assert_writable()
2656+ self._modified = True
2657+ map = self._hardmap
2658+ self._traverse(map, path, setvalue=value)
2659+
2660+ def add(self, path, value, unique=False, soft=False, weak=False):
2661+ assert path
2662+ if type(path) is str:
2663+ path = path_string_to_tuple(path)
2664+ if soft:
2665+ map = self._softmap
2666+ elif weak:
2667+ map = self._weakmap
2668+ else:
2669+ self.assert_writable()
2670+ self._modified = True
2671+ map = self._hardmap
2672+ if unique:
2673+ current = self._traverse(map, path)
2674+ if type(current) is list and value in current:
2675+ return
2676+ path = path+(sys.maxsize,)
2677+ self._traverse(map, path, setvalue=value)
2678+
2679+ def remove(self, path, value=NOTHING, soft=False, weak=False):
2680+ assert path
2681+ if type(path) is str:
2682+ path = path_string_to_tuple(path)
2683+ if soft:
2684+ map = self._softmap
2685+ elif weak:
2686+ map = self._weakmap
2687+ else:
2688+ self.assert_writable()
2689+ self._modified = True
2690+ map = self._hardmap
2691+ marker = NOTHING
2692+ while path:
2693+ if value is marker:
2694+ obj = self._traverse(map, path[:-1])
2695+ elem = path[-1]
2696+ isvalue = False
2697+ else:
2698+ obj = self._traverse(map, path)
2699+ elem = value
2700+ isvalue = True
2701+ result = False
2702+ if obj is not marker:
2703+ result = self._backend.remove(obj, elem, isvalue)
2704+ if result is NotImplemented:
2705+ raise PersistError("Can't remove %r from %r" %
2706+ (elem, type(obj)))
2707+ if self._backend.empty(obj):
2708+ if value is not marker:
2709+ value = marker
2710+ else:
2711+ path = path[:-1]
2712+ else:
2713+ break
2714+ return result
2715+
2716+ def move(self, oldpath, newpath, soft=False, weak=False):
2717+ if not (soft or weak):
2718+ self.assert_writable()
2719+ if type(oldpath) is str:
2720+ oldpath = path_string_to_tuple(oldpath)
2721+ if type(newpath) is str:
2722+ newpath = path_string_to_tuple(newpath)
2723+ result = False
2724+ marker = NOTHING
2725+ value = self._getvalue(oldpath, soft, not (soft or weak), weak)
2726+ if value is not marker:
2727+ self.remove(oldpath, soft=soft, weak=weak)
2728+ self.set(newpath, value, weak, soft)
2729+ result = True
2730+ return result
2731+
2732+ def root_at(self, path):
2733+ return RootedPersist(self, path)
2734+
2735+
2736+class RootedPersist:
2737+
2738+ def __init__(self, parent, root):
2739+ self.parent = parent
2740+ if type(root) is str:
2741+ self.root = path_string_to_tuple(root)
2742+ else:
2743+ self.root = root
2744+
2745+ readonly = property(lambda self: self.parent.readonly)
2746+ modified = property(lambda self: self.parent.modified)
2747+
2748+ def assert_writable(self):
2749+ self.parent.assert_writable()
2750+
2751+ def save(self):
2752+ self.parent.save()
2753+
2754+ def has(self, path, value=NOTHING, soft=False, hard=False, weak=False):
2755+ if type(path) is str:
2756+ path = path_string_to_tuple(path)
2757+ return self.parent.has(self.root+path, value, soft, hard, weak)
2758+
2759+ def keys(self, path, soft=False, hard=False, weak=False):
2760+ if type(path) is str:
2761+ path = path_string_to_tuple(path)
2762+ return self.parent.keys(self.root+path, soft, hard, weak)
2763+
2764+ def get(self, path, default=None, soft=False, hard=False, weak=False):
2765+ if type(path) is str:
2766+ path = path_string_to_tuple(path)
2767+ return self.parent.get(self.root+path, default, soft, hard, weak)
2768+
2769+ def set(self, path, value, soft=False, weak=False):
2770+ if type(path) is str:
2771+ path = path_string_to_tuple(path)
2772+ return self.parent.set(self.root+path, value, soft, weak)
2773+
2774+ def add(self, path, value, unique=False, soft=False, weak=False):
2775+ if type(path) is str:
2776+ path = path_string_to_tuple(path)
2777+ return self.parent.add(self.root+path, value, unique, soft, weak)
2778+
2779+ def remove(self, path, value=NOTHING, soft=False, weak=False):
2780+ if type(path) is str:
2781+ path = path_string_to_tuple(path)
2782+ return self.parent.remove(self.root+path, value, soft, weak)
2783+
2784+ def move(self, oldpath, newpath, soft=False, weak=False):
2785+ if type(oldpath) is str:
2786+ oldpath = path_string_to_tuple(oldpath)
2787+ if type(newpath) is str:
2788+ newpath = path_string_to_tuple(newpath)
2789+ return self.parent.move(self.root+oldpath, self.root+newpath,
2790+ soft, weak)
2791+
2792+ def root_at(self, path):
2793+ if type(path) is str:
2794+ path = path_string_to_tuple(path)
2795+ return self.parent.root_at(self.root+path)
2796+
2797+
2798+_splitpath = re.compile(r"(\[-?\d+\])|(?<!\\)\.").split
2799+
2800+def path_string_to_tuple(path):
2801+ if "." not in path and "[" not in path:
2802+ return (path,)
2803+ result = []
2804+ tokens = _splitpath(path)
2805+ for token in tokens:
2806+ if token:
2807+ if token[0] == "[" and token[-1] == "]":
2808+ try:
2809+ result.append(int(token[1:-1]))
2810+ except ValueError:
2811+ raise PersistError("Invalid path index: %r" % token)
2812+ else:
2813+ result.append(token.replace(r"\.", "."))
2814+ return tuple(result)
2815+
2816+
2817+def path_tuple_to_string(path):
2818+ result = []
2819+ for elem in path:
2820+ if type(elem) is int:
2821+ result[-1] += "[%d]" % elem
2822+ else:
2823+ result.append(str(elem).replace(".", "\."))
2824+ return ".".join(result)
2825+
2826+
2827+class Backend:
2828+
2829+ def new(self):
2830+ return {}
2831+
2832+ def load(self, filepath):
2833+ raise NotImplementedError
2834+
2835+ def save(self, filepath, map):
2836+ raise NotImplementedError
2837+
2838+ def get(self, obj, elem, _marker=NOTHING):
2839+ if type(obj) is dict:
2840+ newobj = obj.get(elem, _marker)
2841+ elif type(obj) in (tuple, list):
2842+ if type(elem) is int:
2843+ try:
2844+ newobj = obj[elem]
2845+ except IndexError:
2846+ newobj = _marker
2847+ elif elem in obj:
2848+ newobj = elem
2849+ else:
2850+ newobj = _marker
2851+ else:
2852+ newobj = NotImplemented
2853+ return newobj
2854+
2855+ def set(self, obj, elem, value):
2856+ if type(obj) is dict:
2857+ newobj = obj[elem] = value
2858+ elif type(obj) is list and type(elem) is int:
2859+ lenobj = len(obj)
2860+ if lenobj <= elem:
2861+ obj.append(None)
2862+ elem = lenobj
2863+ elif elem < 0 and abs(elem) > lenobj:
2864+ obj.insert(0, None)
2865+ elem = 0
2866+ newobj = obj[elem] = value
2867+ else:
2868+ newobj = NotImplemented
2869+ return newobj
2870+
2871+ def remove(self, obj, elem, isvalue):
2872+ result = False
2873+ if type(obj) is dict:
2874+ if elem in obj:
2875+ del obj[elem]
2876+ result = True
2877+ elif type(obj) is list:
2878+ if not isvalue and type(elem) is int:
2879+ try:
2880+ del obj[elem]
2881+ result = True
2882+ except IndexError:
2883+ pass
2884+ elif elem in obj:
2885+ obj[:] = [x for x in obj if x != elem]
2886+ result = True
2887+ else:
2888+ result = NotImplemented
2889+ return result
2890+
2891+ def copy(self, value):
2892+ if type(value) in (dict, list):
2893+ return copy.deepcopy(value)
2894+ return value
2895+
2896+ def empty(self, obj):
2897+ return (not obj)
2898+
2899+ def has(self, obj, elem):
2900+ contains = getattr(obj, "__contains__", None)
2901+ if contains:
2902+ return contains(elem)
2903+ return NotImplemented
2904+
2905+ def keys(self, obj):
2906+ keys = getattr(obj, "keys", None)
2907+ if keys:
2908+ return keys()
2909+ elif type(obj) is list:
2910+ return list(range(len(obj)))
2911+ return NotImplemented
2912+
2913+
2914+class MemoryBackend(Backend):
2915+
2916+ def __init__(self):
2917+ self._store = {}
2918+
2919+ def load(self, filepath):
2920+ return self._store.get(filepath)
2921+
2922+ def save(self, filepath, map):
2923+ self._store[filepath] = map
2924+
2925+class DiskBackend(Backend):
2926+
2927+ safe_file_closing = True
2928+
2929+class PickleBackend(DiskBackend):
2930+
2931+ def __init__(self):
2932+ import pickle
2933+ self._pickle = cPickle
2934+
2935+ def load(self, filepath):
2936+ file = open(filepath, "rb")
2937+ try:
2938+ return self._pickle.load(file)
2939+ finally:
2940+ safe_close(file, self.safe_file_closing)
2941+
2942+ def save(self, filepath, map):
2943+ file = open(filepath, "wb")
2944+ try:
2945+ self._pickle.dump(map, file, 2)
2946+ finally:
2947+ safe_close(file, self.safe_file_closing)
2948+
2949+
2950+class BPickleBackend(DiskBackend):
2951+
2952+ def __init__(self):
2953+ from checkbox.contrib import bpickle
2954+ self._bpickle = bpickle
2955+
2956+ def load(self, filepath):
2957+ file = open(filepath, "rb")
2958+ try:
2959+ return self._bpickle.loads(file.read())
2960+ finally:
2961+ safe_close(file, self.safe_file_closing)
2962+
2963+ def save(self, filepath, map):
2964+ file = open(filepath, "wb")
2965+ try:
2966+ file.write(self._bpickle.dumps(map))
2967+ finally:
2968+ safe_close(file, self.safe_file_closing)
2969
2970=== added file 'checkbox/contrib/xrandr.py'
2971--- checkbox/contrib/xrandr.py 1970-01-01 00:00:00 +0000
2972+++ checkbox/contrib/xrandr.py 2013-10-01 18:38:21 +0000
2973@@ -0,0 +1,1064 @@
2974+#!/usr/bin/python3
2975+# -*- coding: utf-8 -*-
2976+#
2977+# Python-XRandR provides a high level API for the XRandR extension of the
2978+# X.org server. XRandR allows to configure resolution, refresh rate, rotation
2979+# of the screen and multiple outputs of graphics cards.
2980+#
2981+# Copyright 2007 © Sebastian Heinlein <sebastian.heinlein@web.de>
2982+# Copyright 2007 © Michael Vogt <mvo@ubuntu.com>
2983+# Copyright 2007 © Canonical Ltd.
2984+#
2985+# In many aspects it follows the design of the xrand tool of the X.org, which
2986+# comes with the following copyright:
2987+#
2988+# Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
2989+# Copyright © 2002 Hewlett Packard Company, Inc.
2990+# Copyright © 2006 Intel Corporation
2991+#
2992+# And can be downloaded here:
2993+#
2994+# git://anongit.freedesktop.org/git/xorg/app/xrandr
2995+#
2996+# This library is free software; you can redistribute it and/or
2997+# modify it under the terms of the GNU Lesser General Public
2998+# License as published by the Free Software Foundation; either
2999+# version 2.1 of the License, or any later version.
3000+#
3001+# This library is distributed in the hope that it will be useful,
3002+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3003+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3004+# Lesser General Public License for more details.
3005+#
3006+# You should have received a copy of the GNU Lesser General Public
3007+# License along with this library; if not, write to the Free Software
3008+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
3009+# MA 02110-1301 USA
3010+
3011+from ctypes import (
3012+ POINTER,
3013+ Structure,
3014+ byref,
3015+ c_char_p,
3016+ c_void_p,
3017+ c_int,
3018+ c_long,
3019+ c_ulong,
3020+ c_ushort,
3021+ cdll,
3022+)
3023+import os
3024+
3025+RR_ROTATE_0 = 1
3026+RR_ROTATE_90 = 2
3027+RR_ROTATE_180 = 4
3028+RR_ROTATE_270 = 8
3029+RR_REFLECT_X = 16
3030+RR_REFLECT_Y = 32
3031+
3032+RR_CONNECTED = 0
3033+RR_DISCONNECTED = 1
3034+RR_UNKOWN_CONNECTION = 2
3035+
3036+RR_BAD_OUTPUT = 0
3037+RR_BAD_CRTC = 1
3038+RR_BAD_MODE = 2
3039+
3040+RR_SET_CONFIG_SUCCESS = 0
3041+RR_SET_CONFIG_INVALID_CONFIG_TIME = 1
3042+RR_SET_CONFIG_INVALID_TIME = 2
3043+RR_SET_CONFIG_FAILED = 3
3044+
3045+# Flags to keep track of changes
3046+CHANGES_NONE = 0
3047+CHANGES_CRTC = 1
3048+CHANGES_MODE = 2
3049+CHANGES_RELATION = 4
3050+CHANGES_POSITION = 8
3051+CHANGES_ROTATION = 16
3052+CHANGES_REFLECTION = 32
3053+CHANGES_AUTOMATIC = 64
3054+CHANGES_REFRESH = 128
3055+CHANGES_PROPERTY = 256
3056+
3057+# Relation information
3058+RELATION_ABOVE = 0
3059+RELATION_BELOW = 1
3060+RELATION_RIGHT_OF = 2
3061+RELATION_LEFT_OF = 3
3062+RELATION_SAME_AS = 4
3063+
3064+# some fundamental datatypes
3065+RRCrtc = c_long
3066+RROutput = c_long
3067+RRMode = c_long
3068+Connection = c_ushort
3069+SubpixelOrder = c_ushort
3070+Time = c_ulong
3071+Rotation = c_ushort
3072+Status = c_int
3073+
3074+# load the libs
3075+xlib = cdll.LoadLibrary('libX11.so.6')
3076+rr = cdll.LoadLibrary('libXrandr.so.2')
3077+
3078+
3079+# query resources
3080+class _XRRModeInfo(Structure):
3081+ _fields_ = [
3082+ ("id", RRMode), # XID is c_long
3083+ ("width", c_int),
3084+ ("height", c_int),
3085+ ("dotClock", c_long),
3086+ ("hSyncStart", c_int),
3087+ ("hSyncEnd", c_int),
3088+ ("hTotal", c_int),
3089+ ("hSkew", c_int),
3090+ ("vSyncStart", c_int),
3091+ ("vSyncEnd", c_int),
3092+ ("vTotal", c_int),
3093+ ("name", c_char_p),
3094+ ("nameLength", c_int),
3095+ ("modeFlags", c_long),
3096+ ]
3097+
3098+
3099+class _XRRScreenSize(Structure):
3100+ _fields_ = [
3101+ ("width", c_int),
3102+ ("height", c_int),
3103+ ("mwidth", c_int),
3104+ ("mheight", c_int)
3105+ ]
3106+
3107+
3108+class _XRRCrtcInfo(Structure):
3109+ _fields_ = [
3110+ ("timestamp", Time),
3111+ ("x", c_int),
3112+ ("y", c_int),
3113+ ("width", c_int),
3114+ ("height", c_int),
3115+ ("mode", RRMode),
3116+ ("rotation", c_int),
3117+ ("noutput", c_int),
3118+ ("outputs", POINTER(RROutput)),
3119+ ("rotations", Rotation),
3120+ ("npossible", c_int),
3121+ ("possible", POINTER(RROutput)),
3122+ ]
3123+
3124+
3125+class _XRRScreenResources(Structure):
3126+ _fields_ = [
3127+ ("timestamp", Time),
3128+ ("configTimestamp", Time),
3129+ ("ncrtc", c_int),
3130+ ("crtcs", POINTER(RRCrtc)),
3131+ ("noutput", c_int),
3132+ ("outputs", POINTER(RROutput)),
3133+ ("nmode", c_int),
3134+ ("modes", POINTER(_XRRModeInfo)),
3135+ ]
3136+
3137+
3138+class RRError(Exception):
3139+ """Base exception class of the module"""
3140+ pass
3141+
3142+
3143+class UnsupportedRRError(RRError):
3144+ """Raised if the required XRandR extension version is not available"""
3145+ def __init__(self, required, current):
3146+ self.required = required
3147+ self.current = current
3148+
3149+
3150+# XRRGetOutputInfo
3151+class _XRROutputInfo(Structure):
3152+ _fields_ = [
3153+ ("timestamp", Time),
3154+ ("crtc", c_int),
3155+ ("name", c_char_p),
3156+ ("nameLen", c_int),
3157+ ("mm_width", c_ulong),
3158+ ("mm_height", c_ulong),
3159+ ("connection", Connection),
3160+ ("subpixel_order", SubpixelOrder),
3161+ ("ncrtc", c_int),
3162+ ("crtcs", POINTER(RRCrtc)),
3163+ ("nclone", c_int),
3164+ ("clones", POINTER(RROutput)),
3165+ ("nmode", c_int),
3166+ ("npreferred", c_int),
3167+ ("modes", POINTER(RRMode))
3168+ ]
3169+
3170+
3171+class _XRRCrtcGamma(Structure):
3172+ _fields_ = [
3173+ ('size', c_int),
3174+ ('red', POINTER(c_ushort)),
3175+ ('green', POINTER(c_ushort)),
3176+ ('blue', POINTER(c_ushort)),
3177+ ]
3178+
3179+
3180+def _array_conv(array, type, conv=lambda x: x):
3181+ length = len(array)
3182+ res = (type * length)()
3183+ for i in range(length):
3184+ res[i] = conv(array[i])
3185+ return res
3186+
3187+
3188+class Output:
3189+ """The output is a reference to a supported output jacket of the graphics
3190+ card. Outputs are attached to a hardware pipe to be used. Furthermore
3191+ they can be a clone of another output or show a subset of the screen"""
3192+ def __init__(self, info, id, screen):
3193+ """Initializes an output instance"""
3194+ self._info = info
3195+ self.id = id
3196+ self._screen = screen
3197+ # Store changes later here
3198+ self._mode = None
3199+ self._crtc = None
3200+ self._rotation = RR_ROTATE_0
3201+ self._relation = None
3202+ self._relation_offset = 0
3203+ self._relative_to = None
3204+ self._position = None
3205+ self._reflection = None
3206+ self._automatic = None
3207+ self._rate = None
3208+ self._changes = CHANGES_NONE
3209+ self._x = 0
3210+ self._y = 0
3211+
3212+ self.name = self._info.contents.name
3213+
3214+ def __del__(self):
3215+ """Frees the internal reference to the output info if the output gets
3216+ removed"""
3217+ rr.XRRFreeOutputInfo(self._info)
3218+
3219+ def get_physical_width(self):
3220+ """Returns the display width reported by the connected output device"""
3221+ return self._info.contents.mm_width
3222+
3223+ def get_physical_height(self):
3224+ """
3225+ Returns the display height reported by the connected output device
3226+ """
3227+ return self._info.contents.mm_height
3228+
3229+ def get_crtc(self):
3230+ """Returns the xid of the hardware pipe to which the output is
3231+ attached. If the output is disabled it will return 0"""
3232+ return self._info.contents.crtc
3233+
3234+ def get_crtcs(self):
3235+ """Returns the xids of the hardware pipes to which the output could
3236+ be attached"""
3237+ crtcs = []
3238+ for i in range(self._info.contents.ncrtc):
3239+ for crtc in self._screen.crtcs:
3240+ if crtc.xid == self._info.contents.crtcs[i]:
3241+ crtcs.append(crtc)
3242+ return crtcs
3243+
3244+ def get_available_rotations(self):
3245+ """Returns a binary flag of the supported rotations of the output or
3246+ 0 if the output is disabled"""
3247+ rotations = RR_ROTATE_0
3248+ found = False
3249+ if self.is_active():
3250+ # Get the rotations supported by all crtcs to make assigning
3251+ # crtcs easier. Furthermore there don't seem to be so many
3252+ # cards which show another behavior
3253+ for crtc in self.get_crtcs():
3254+ # Set rotations to the value of the first found crtc and
3255+ # then create a subset only for all other crtcs
3256+ if not found:
3257+ rotations = crtc.get_available_rotations()
3258+ found = True
3259+ else:
3260+ rotations = rotations & crtc.get_available_rotations()
3261+ return rotations
3262+
3263+ def get_available_modes(self):
3264+ """Returns the list of supported mode lines (resolution, refresh rate)
3265+ that are supported by the connected device"""
3266+ modes = []
3267+ for m in range(self._info.contents.nmode):
3268+ output_modes = self._info.contents.modes
3269+ for s in range(self._screen._resources.contents.nmode):
3270+ screen_modes = self._screen._resources.contents.modes
3271+ if screen_modes[s].id == output_modes[m]:
3272+ modes.append(screen_modes[s])
3273+ return modes
3274+
3275+ def get_preferred_mode(self):
3276+ """Returns an index that refers to the list of available modes and
3277+ points to the preferred mode of the connected device"""
3278+ return self._info.contents.npreferred
3279+
3280+ def is_active(self):
3281+ """Returns True if the output is attached to a hardware pipe, is
3282+ enabled"""
3283+ return self._info.contents.crtc != 0
3284+
3285+ def is_connected(self):
3286+ """Return True if a device is detected at the output"""
3287+ if self._info.contents.connection in (RR_CONNECTED,
3288+ RR_UNKOWN_CONNECTION):
3289+ return True
3290+ return False
3291+
3292+ def disable(self):
3293+ """Disables the output"""
3294+ if not self.is_active():
3295+ return
3296+ self._mode = None
3297+ self._crtc._outputs.remove(self)
3298+ self._crtc = None
3299+ self._changes = self._changes | CHANGES_CRTC | CHANGES_MODE
3300+
3301+ def set_to_mode(self, mode):
3302+ modes = self.get_available_modes()
3303+ if mode in range(len(modes)):
3304+ self._mode = modes[mode].id
3305+ return
3306+ raise RRError("Mode is not available")
3307+
3308+ def set_to_preferred_mode(self):
3309+ modes = self.get_available_modes()
3310+ mode = modes[self.get_preferred_mode()]
3311+ if mode != None:
3312+ self._mode = mode.id
3313+ return
3314+ raise RRError("Preferred mode is not available")
3315+
3316+ def get_clones(self):
3317+ """Returns the xids of the outputs which can be clones of the output"""
3318+ clones = []
3319+ for i in range(self._info.contents.nclone):
3320+ id = self._info.contents.clones[i]
3321+ o = self._screen.get_output_by_id(id)
3322+ clones.append(o)
3323+ return clones
3324+
3325+ def set_relation(self, relative, relation, offset=0):
3326+ """Sets the position of the output in relation to the given one"""
3327+ rel = self._screen.get_output_by_name(relative)
3328+ if rel and relation in (RELATION_LEFT_OF, RELATION_RIGHT_OF,
3329+ RELATION_ABOVE, RELATION_BELOW,
3330+ RELATION_SAME_AS):
3331+ self._relation = relation
3332+ self._relative_to = rel
3333+ self._relation_offset = offset
3334+ self._changes = self._changes | CHANGES_RELATION
3335+ else:
3336+ raise RRError("The given relative output or relation is not "
3337+ "available")
3338+
3339+ def has_changed(self, changes=None):
3340+ """Checks if the output has changed: Either for a specific change or
3341+ generally"""
3342+ if changes:
3343+ return self._changes & changes
3344+ else:
3345+ return self._changes != CHANGES_NONE
3346+
3347+
3348+class Crtc:
3349+ """The crtc is a reference to a hardware pipe that is provided by the
3350+ graphics device. Outputs can be attached to crtcs"""
3351+
3352+ def __init__(self, info, xid, screen):
3353+ """Initializes the hardware pipe object"""
3354+ self._info = info
3355+ self.xid = xid
3356+ self._screen = screen
3357+ self._outputs = []
3358+
3359+ def __del__(self):
3360+ """Frees the reference to the rendering pipe if the instance gets
3361+ removed"""
3362+ rr.XRRFreeCrtcConfigInfo(self._info)
3363+
3364+ def get_xid(self):
3365+ """Returns the internal id of the crtc from the X server"""
3366+ return self.xid
3367+
3368+ def get_available_rotations(self):
3369+ """Returns a binary flag that contains the supported rotations of the
3370+ hardware pipe"""
3371+ return self._info.contents.rotations
3372+
3373+ def set_config(self, x, y, mode, outputs, rotation=RR_ROTATE_0):
3374+ """Configures the render pipe with the given mode and outputs. X and y
3375+ set the position of the crtc output in the screen"""
3376+ rr.XRRSetCrtcConfig(self._screen._display,
3377+ self._screen._resources,
3378+ self.xid,
3379+ self._screen.get_timestamp(),
3380+ c_int(x), c_int(y),
3381+ mode,
3382+ rotation,
3383+ _array_conv(outputs, RROutput, lambda x: x.id),
3384+ len(outputs))
3385+
3386+ def apply_changes(self):
3387+ """Applies the stored changes"""
3388+ if len(self._outputs) > 0:
3389+ output = self._outputs[0]
3390+ self.set_config(output._x, output._y, output._mode,
3391+ self._outputs, output._rotation)
3392+ else:
3393+ self.disable()
3394+
3395+ def disable(self):
3396+ """Turns off all outputs on the crtc"""
3397+ rr.XRRSetCrtcConfig(self._screen._display,
3398+ self._screen._resources,
3399+ self.xid,
3400+ self._screen.get_timestamp(),
3401+ 0, 0,
3402+ None,
3403+ RR_ROTATE_0,
3404+ 0, 0)
3405+
3406+ #FIXME: support gamma settings
3407+ """
3408+ def get_gamma_size(self):
3409+ return rr.XRRGetCrtcGammaSize(self._screen._display, self.id)
3410+ def get_gamma(self):
3411+ result = rr.XRRGetCrtcGamma(self._screen._display, self.id)
3412+ return _from_gamma(result)
3413+ def set_gamma(self, gamma):
3414+ g = _to_gamma(gamma)
3415+ rr.XRRSetCrtcGamma(self._screen._display, self.id, g)
3416+ rr.XRRFreeGamma(g)
3417+ gamma = property(get_gamma, set_gamma)"""
3418+
3419+ def load_outputs(self):
3420+ """Get the currently assigned outputs"""
3421+ outputs = []
3422+ for i in range(self._info.contents.noutput):
3423+ id = self._info.contents.outputs[i]
3424+ o = self._screen.get_output_by_id(id)
3425+ outputs.append(o)
3426+ self._outputs = outputs
3427+
3428+ def get_outputs(self):
3429+ """Returns the list of attached outputs"""
3430+ return self._outputs
3431+
3432+ def add_output(self, output):
3433+ """Adds the specified output to the crtc"""
3434+ output._crtc = self
3435+ self._outputs.append(output)
3436+
3437+ def supports_output(self, output):
3438+ """Check if the output can be used by the crtc.
3439+ See check_crtc_for_output in xrandr.c"""
3440+ if not self.xid in [c.xid for c in output.get_crtcs()]:
3441+ return False
3442+ if len(self._outputs):
3443+ for other in self._outputs:
3444+ if other == output:
3445+ continue
3446+ if other._x != output._x:
3447+ return False
3448+ if other._y != output._y:
3449+ return False
3450+ if other._mode != output._mode:
3451+ return False
3452+ if other._rotation != output._rotation:
3453+ return False
3454+ #FIXME: pick_crtc is still missing
3455+ elif self._info.contents.noutput > 0:
3456+ if self._info.contents.x != output._x:
3457+ return False
3458+ if self._info.contents.y != output._y:
3459+ return False
3460+ if self._info.contents.mode_info != output._mode:
3461+ return False
3462+ if self._info.rotation != output._rotation:
3463+ return False
3464+ return True
3465+
3466+ def supports_rotation(self, rotation):
3467+ """Check if the given rotation is supported by the crtc"""
3468+ rotations = self._info.contents.rotations
3469+ dir = rotation & (RR_ROTATE_0 | RR_ROTATE_90 | RR_ROTATE_180 |
3470+ RR_ROTATE_270)
3471+ reflect = rotation & (RR_REFLECT_X | RR_REFLECT_Y)
3472+ if (((rotations & dir) != 0) and ((rotations & reflect) == reflect)):
3473+ return True
3474+ return False
3475+
3476+ def has_changed(self):
3477+ """Check if there are any new outputs assigned to the crtc or any
3478+ outputs with a changed mode or position"""
3479+ if len(self._outputs) != self._info.contents.noutput:
3480+ return True
3481+ for i in range(self._info.contents.noutput):
3482+ id = self._info.contents.outputs[i]
3483+ output = self._screen.get_output_by_id(id)
3484+ if not output in self._outputs:
3485+ return True
3486+ if output.has_changed():
3487+ return True
3488+ return False
3489+
3490+
3491+class Screen:
3492+ def __init__(self, dpy, screen=-1):
3493+ """Initializes the screen"""
3494+ # Some sane default values
3495+ self.outputs = {}
3496+ self.crtcs = []
3497+ self._width = 0
3498+ self._height = 0
3499+ self._width_max = 0
3500+ self._height_max = 0
3501+ self._width_min = 0
3502+ self._height_min = 0
3503+ self._width_mm = 0
3504+ self._height_mm = 0
3505+
3506+ self._display = dpy
3507+ if not -1 <= screen < xlib.XScreenCount(dpy):
3508+ raise RRError("The chosen screen is not available", screen)
3509+ elif screen == -1:
3510+ self._screen = xlib.XDefaultScreen(dpy)
3511+ else:
3512+ self._screen = screen
3513+ self._root = xlib.XDefaultRootWindow(self._display, self._screen)
3514+ self._id = rr.XRRRootToScreen(self._display, self._root)
3515+
3516+ self._load_resources()
3517+ self._load_config()
3518+ (self._width, self._height,
3519+ self._width_mm, self._height_mm) = self.get_size()
3520+ if XRANDR_VERSION >= (1, 2):
3521+ self._load_screen_size_range()
3522+ self._load_crtcs()
3523+ self._load_outputs()
3524+
3525+ # Store XRandR 1.0 changes here
3526+ self._rate = self.get_current_rate()
3527+ self._rotation = self.get_current_rotation()
3528+ self._size_index = self.get_current_size_index()
3529+
3530+ def __del__(self):
3531+ """Free the reference to the interal screen config if the screen
3532+ gets removed"""
3533+ rr.XRRFreeScreenConfigInfo(self._config)
3534+
3535+ def _load_config(self):
3536+ """Loads the screen configuration. Only needed privately by the
3537+ the bindings"""
3538+ class XRRScreenConfiguration(Structure):
3539+ " private to Xrandr "
3540+ pass
3541+ gsi = rr.XRRGetScreenInfo
3542+ gsi.restype = POINTER(XRRScreenConfiguration)
3543+ self._config = gsi(self._display, self._root)
3544+
3545+ def _load_screen_size_range(self):
3546+ """Detects the dimensionios of the screen"""
3547+ minWidth = c_int()
3548+ minHeight = c_int()
3549+ maxWidth = c_int()
3550+ maxHeight = c_int()
3551+ res = rr.XRRGetScreenSizeRange(self._display, self._root,
3552+ byref(minWidth), byref(minHeight),
3553+ byref(maxWidth), byref(maxHeight))
3554+ if res:
3555+ self._width_max = maxWidth.value
3556+ self._width_min = minWidth.value
3557+ self._height_max = maxHeight.value
3558+ self._height_min = minHeight.value
3559+
3560+ def _load_resources(self):
3561+ """Loads the screen resources. Only needed privately for the
3562+ bindings"""
3563+ gsr = rr.XRRGetScreenResources
3564+ gsr.restype = POINTER(_XRRScreenResources)
3565+ self._resources = gsr(self._display, self._root)
3566+
3567+ def _load_crtcs(self):
3568+ """Loads the available XRandR 1.2 crtcs (hardware pipes) of
3569+ the screen"""
3570+ gci = rr.XRRGetCrtcInfo
3571+ gci.restype = POINTER(_XRRCrtcInfo)
3572+ c = self._resources.contents.crtcs
3573+ for i in range(self._resources.contents.ncrtc):
3574+ xrrcrtcinfo = gci(self._display, self._resources, c[i])
3575+ self.crtcs.append(Crtc(xrrcrtcinfo, c[i], self))
3576+
3577+ def _load_outputs(self):
3578+ """Loads the available XRandR 1.2 outputs of the screen"""
3579+ goi = rr.XRRGetOutputInfo
3580+ goi.restype = POINTER(_XRROutputInfo)
3581+ o = self._resources.contents.outputs
3582+ for i in range(self._resources.contents.noutput):
3583+ xrroutputinfo = goi(self._display, self._resources, o[i])
3584+ output = Output(xrroutputinfo, o[i], self)
3585+ self.outputs[xrroutputinfo.contents.name] = output
3586+ # Store the mode of the crtc in the output instance
3587+ crtc = self.get_crtc_by_xid(output.get_crtc())
3588+ if crtc:
3589+ output._mode = crtc._info.contents.mode
3590+ crtc.add_output(output)
3591+
3592+ def get_size(self):
3593+ """Returns the current pixel and physical size of the screen"""
3594+ width = xlib.XDisplayWidth(self._display, self._screen)
3595+ width_mm = xlib.XDisplayWidthMM(self._display, self._screen)
3596+ height = xlib.XDisplayHeight(self._display, self._screen)
3597+ height_mm = xlib.XDisplayHeightMM(self._display, self._screen)
3598+ return width, height, width_mm, height_mm
3599+
3600+ def get_timestamp(self):
3601+ """Creates a X timestamp that must be used when applying changes, since
3602+ they can be delayed"""
3603+ config_timestamp = Time()
3604+ rr.XRRTimes.restpye = c_ulong
3605+ return rr.XRRTimes(self._display, self._id, byref(config_timestamp))
3606+
3607+ def get_crtc_by_xid(self, xid):
3608+ """Returns the crtc with the given xid or None"""
3609+ for crtc in self.crtcs:
3610+ if crtc.xid == xid:
3611+ return crtc
3612+ return None
3613+
3614+ def get_current_rate(self):
3615+ """Returns the currently used refresh rate"""
3616+ _check_required_version((1, 0))
3617+ xccr = rr.XRRConfigCurrentRate
3618+ xccr.restype = c_int
3619+ return xccr(self._config)
3620+
3621+ def get_available_rates_for_size_index(self, size_index):
3622+ """Returns the refresh rates that are supported by the screen for
3623+ the given resolution. See get_available_sizes for the resolution to
3624+ which size_index points"""
3625+ _check_required_version((1, 0))
3626+ rates = []
3627+ nrates = c_int()
3628+ rr.XRRConfigRates.restype = POINTER(c_ushort)
3629+ _rates = rr.XRRConfigRates(self._config, size_index, byref(nrates))
3630+ for r in range(nrates.value):
3631+ rates.append(_rates[r])
3632+ return rates
3633+
3634+ def get_current_rotation(self):
3635+ """Returns the currently used rotation. Can be RR_ROTATE_0,
3636+ RR_ROTATE_90, RR_ROTATE_180 or RR_ROTATE_270"""
3637+ _check_required_version((1, 0))
3638+ current = c_ushort()
3639+ rr.XRRConfigRotations(self._config, byref(current))
3640+ return current.value
3641+
3642+ def get_available_rotations(self):
3643+ """Returns a binary flag that holds the available resolutions"""
3644+ _check_required_version((1, 0))
3645+ current = c_ushort()
3646+ rotations = rr.XRRConfigRotations(self._config, byref(current))
3647+ return rotations
3648+
3649+ def get_current_size_index(self):
3650+ """Returns the position of the currently used resolution size in the
3651+ list of available resolutions. See get_available_sizes"""
3652+ _check_required_version((1, 0))
3653+ rotation = c_ushort()
3654+ size = rr.XRRConfigCurrentConfiguration(self._config,
3655+ byref(rotation))
3656+ return size
3657+
3658+ def get_available_sizes(self):
3659+ """Returns the available resolution sizes of the screen. The size
3660+ index points to the corresponding resolution of this list"""
3661+ _check_required_version((1, 0))
3662+ sizes = []
3663+ nsizes = c_int()
3664+ xcs = rr.XRRConfigSizes
3665+ xcs.restype = POINTER(_XRRScreenSize)
3666+ _sizes = xcs(self._config, byref(nsizes))
3667+ for r in range(nsizes.value):
3668+ sizes.append(_sizes[r])
3669+ return sizes
3670+
3671+ def set_config(self, size_index, rate, rotation):
3672+ """Configures the screen with the given resolution at the given size
3673+ index, rotation and refresh rate. To get in effect call
3674+ Screen.apply_config()"""
3675+ _check_required_version((1, 0))
3676+ self.set_size_index(size_index)
3677+ self.set_refresh_rate(rate)
3678+ self.set_rotation(rotation)
3679+
3680+ def set_size_index(self, index):
3681+ """Sets the reoslution of the screen. To get in effect call
3682+ Screen.apply_config()"""
3683+ if index in range(len(self.get_available_sizes())):
3684+ self._size_index = index
3685+ else:
3686+ raise RRError("There isn't any size associated "
3687+ "to the index %s" % index)
3688+
3689+ def set_rotation(self, rotation):
3690+ """Sets the rotation of the screen. To get in effect call
3691+ Screen.apply_config()"""
3692+ if self.get_available_rotations() & rotation:
3693+ self._rotation = rotation
3694+ else:
3695+ raise RRError("The chosen rotation is not supported")
3696+
3697+ def set_refresh_rate(self, rate):
3698+ """Sets the refresh rate of the screen. To get in effect call
3699+ Screen.apply_config()"""
3700+ if rate in self.get_available_rates_for_size_index(self._size_index):
3701+ self._rate = rate
3702+ else:
3703+ raise RRError("The chosen refresh rate %s is not "
3704+ "supported" % rate)
3705+
3706+ def get_mode_by_xid(self, xid):
3707+ """Returns the mode of the given xid"""
3708+ screen_modes = self._resources.contents.modes
3709+ for s in range(self._resources.contents.nmode):
3710+ if screen_modes[s].id == xid:
3711+ return screen_modes[s]
3712+ return None
3713+
3714+ def get_output_by_name(self, name):
3715+ """Returns the output of the screen with the given name or None"""
3716+ if name in self.outputs:
3717+ return self.outputs[name]
3718+ else:
3719+ return None
3720+
3721+ def get_output_by_id(self, id):
3722+ """Returns the output of the screen with the given xid or None"""
3723+ for o in list(self.outputs.values()):
3724+ if o.id == id:
3725+ return o
3726+ return None
3727+
3728+ def print_info(self, verbose=False):
3729+ """Prints some information about the detected screen and its outputs"""
3730+ _check_required_version((1, 0))
3731+ print("Screen %s: minimum %s x %s, current %s x %s, maximum %s x %s" %\
3732+ (self._screen,
3733+ self._width_min, self._height_min,
3734+ self._width, self._height,
3735+ self._width_max, self._height_max))
3736+ print(" %smm x %smm" % (self._width_mm, self._height_mm))
3737+ print("Crtcs: %s" % len(self.crtcs))
3738+ if verbose:
3739+ print("Modes (%s):" % self._resources.contents.nmode)
3740+ modes = self._resources.contents.modes
3741+ for i in range(self._resources.contents.nmode):
3742+ print(" %s - %sx%s" % (modes[i].name,
3743+ modes[i].width,
3744+ modes[i].height))
3745+ i = 0
3746+ print("Sizes @ Refresh Rates:")
3747+ for s in self.get_available_sizes():
3748+ print(" [%s] %s x %s @ %s" % (
3749+ i, s.width, s.height,
3750+ self.get_available_rates_for_size_index(i)))
3751+ i += 1
3752+ print("Rotations:")
3753+ rots = self.get_available_rotations()
3754+ if rots & RR_ROTATE_0:
3755+ print("normal")
3756+ if rots & RR_ROTATE_90:
3757+ print("right")
3758+ if rots & RR_ROTATE_180:
3759+ print("inverted")
3760+ if rots & RR_ROTATE_270:
3761+ print("left")
3762+ print("")
3763+ print("Outputs:")
3764+ for o in list(self.outputs.keys()):
3765+ output = self.outputs[o]
3766+ print(" %s" % o)
3767+ if output.is_connected():
3768+ print("(%smm x %smm)" % (output.get_physical_width(),
3769+ output.get_physical_height()))
3770+ modes = output.get_available_modes()
3771+ print(" Modes:")
3772+ for m in range(len(modes)):
3773+ mode = modes[m]
3774+ refresh = mode.dotClock / (mode.hTotal * mode.vTotal)
3775+ print(" [%s] %s x %s @ %s" % (m,
3776+ mode.width,
3777+ mode.height,
3778+ refresh))
3779+ if mode.id == output._mode:
3780+ print("*")
3781+ if m == output.get_preferred_mode():
3782+ print("(preferred)")
3783+ print("")
3784+ print(" Rotations:")
3785+ rots = output.get_available_rotations()
3786+ if rots & RR_ROTATE_0:
3787+ print("normal")
3788+ if rots & RR_ROTATE_90:
3789+ print("right")
3790+ if rots & RR_ROTATE_180:
3791+ print("inverted")
3792+ if rots & RR_ROTATE_270:
3793+ print("left")
3794+ print("")
3795+ else:
3796+ print("(not connected)")
3797+ if verbose:
3798+ print(" Core properties:")
3799+ for (f, t) in output._info.contents._fields_:
3800+ print(" %s: %s" % (
3801+ f, getattr(output._info.contents, f)))
3802+
3803+ def get_outputs(self):
3804+ """Returns the outputs of the screen"""
3805+ _check_required_version((1, 2))
3806+ return list(self.outputs.values())
3807+
3808+ def get_output_names(self):
3809+ _check_required_version((1, 2))
3810+ return list(self.outputs.keys())
3811+
3812+ def set_size(self, width, height, width_mm, height_mm):
3813+ """Apply the given pixel and physical size to the screen"""
3814+ _check_required_version((1, 2))
3815+ # Check if we really need to apply the changes
3816+ if (width, height, width_mm, height_mm) == self.get_size():
3817+ return
3818+ rr.XRRSetScreenSize(self._display, self._root,
3819+ c_int(width), c_int(height),
3820+ c_int(width_mm), c_int(height_mm))
3821+
3822+ def apply_output_config(self):
3823+ """Used for instantly applying RandR 1.2 changes"""
3824+ _check_required_version((1, 2))
3825+ self._arrange_outputs()
3826+ self._calculate_size()
3827+ self.set_size(self._width, self._height,
3828+ self._width_mm, self._height_mm)
3829+
3830+ # Assign all active outputs to crtcs
3831+ for output in list(self.outputs.values()):
3832+ if not output._mode or output._crtc:
3833+ continue
3834+ for crtc in output.get_crtcs():
3835+ if crtc and crtc.supports_output(output):
3836+ crtc.add_output(output)
3837+ output._changes = output._changes | CHANGES_CRTC
3838+ break
3839+ if not output._crtc:
3840+ #FIXME: Take a look at the pick_crtc code in xrandr.c
3841+ raise RRError("There is no matching crtc for the output")
3842+
3843+ # Apply stored changes of crtcs
3844+ for crtc in self.crtcs:
3845+ if crtc.has_changed():
3846+ crtc.apply_changes()
3847+
3848+ def apply_config(self):
3849+ """Used for instantly applying RandR 1.0 changes"""
3850+ _check_required_version((1, 0))
3851+ status = rr.XRRSetScreenConfigAndRate(self._display,
3852+ self._config,
3853+ self._root,
3854+ self._size_index,
3855+ self._rotation,
3856+ self._rate,
3857+ self.get_timestamp())
3858+ return status
3859+
3860+ def _arrange_outputs(self):
3861+ """Arrange all output positions according to their relative position"""
3862+ for output in self.get_outputs():
3863+ # Skip not changed and not used outputs
3864+ if not output.has_changed(CHANGES_RELATION) or \
3865+ output._mode == None:
3866+ continue
3867+ relative = output._relative_to
3868+ mode = self.get_mode_by_xid(output._mode)
3869+ mode_relative = self.get_mode_by_xid(relative._mode)
3870+ if not relative or not relative._mode:
3871+ output._x = 0
3872+ output._y = 0
3873+ output._changes = output._changes | CHANGES_POSITION
3874+ if output._relation == RELATION_LEFT_OF:
3875+ output._y = relative._y + output._relation_offset
3876+ output._x = relative._x - \
3877+ get_mode_width(mode, output._rotation)
3878+ elif output._relation == RELATION_RIGHT_OF:
3879+ output._y = relative._y + output._relation_offset
3880+ output._x = relative._x + get_mode_width(mode_relative,
3881+ output._rotation)
3882+ elif output._relation == RELATION_ABOVE:
3883+ output._y = relative._y - get_mode_height(mode,
3884+ output._rotation)
3885+ output._x = relative._x + output._relation_offset
3886+ elif output._relation == RELATION_BELOW:
3887+ output._y = relative._y + get_mode_height(mode_relative,
3888+ output._rotation)
3889+ output._x = relative._x + output._relation_offset
3890+ elif output._relation == RELATION_SAME_AS:
3891+ output._y = relative._y + output._relation_offset
3892+ output._x = relative._x + output._relation_offset
3893+ output._changes = output._changes | CHANGES_POSITION
3894+ # Normalize the postions so to the upper left cornor of all outputs
3895+ # is at 0,0
3896+ min_x = 32768
3897+ min_y = 32768
3898+ for output in self.get_outputs():
3899+ if output._mode == None:
3900+ continue
3901+ if output._x < min_x:
3902+ min_x = output._x
3903+ if output._y < min_y:
3904+ min_y = output._y
3905+ for output in self.get_outputs():
3906+ if output._mode == None:
3907+ continue
3908+ output._x -= min_x
3909+ output._y -= min_y
3910+ output._changes = output._changes | CHANGES_POSITION
3911+
3912+ def _calculate_size(self):
3913+ """Recalculate the pixel and physical size of the screen so that
3914+ it covers all outputs"""
3915+ width = self._width
3916+ height = self._height
3917+ for output in self.get_outputs():
3918+ if not output._mode:
3919+ continue
3920+ mode = self.get_mode_by_xid(output._mode)
3921+ x = output._x
3922+ y = output._y
3923+ w = get_mode_width(mode, output._rotation)
3924+ h = get_mode_height(mode, output._rotation)
3925+ if x + w > width:
3926+ width = x + w
3927+ if y + h > height:
3928+ height = y + h
3929+ if width > self._width_max or height > self._height_max:
3930+ raise RRError("The required size is not supported",
3931+ (width, height), (self._width_max, self._width_min))
3932+ else:
3933+ if height < self._height_min:
3934+ self._fb_height = self._height_min
3935+ else:
3936+ self._height = height
3937+ if width < self._width_min:
3938+ self._width = self._width_min
3939+ else:
3940+ self._width = width
3941+ #FIXME: Physical size is missing
3942+
3943+
3944+def get_current_display():
3945+ """Returns the currently used display"""
3946+ display_url = os.getenv("DISPLAY")
3947+ open_display = xlib.XOpenDisplay
3948+ # Set .argtypes and .restype, to ensure proper
3949+ # type check and conversion
3950+ open_display.restype = c_void_p
3951+ open_display.argtypes = [c_char_p]
3952+ # XOpenDisplay accepts a char*, but
3953+ # display_url is a unicode string therefore
3954+ # we convert it to a bytes string
3955+ dpy = open_display(display_url.encode('utf-8'))
3956+ return dpy
3957+
3958+
3959+def get_current_screen():
3960+ """Returns the currently used screen"""
3961+ screen = Screen(get_current_display())
3962+ return screen
3963+
3964+
3965+def get_screen_of_display(display, count):
3966+ """Returns the screen of the given display"""
3967+ dpy = xlib.XOpenDisplay(display)
3968+ return Screen(dpy, count)
3969+
3970+
3971+def get_version():
3972+ """Returns a tuple containing the major and minor version of the xrandr
3973+ extension or None if the extension is not available"""
3974+ major = c_int()
3975+ minor = c_int()
3976+ res = rr.XRRQueryVersion(get_current_display(),
3977+ byref(major), byref(minor))
3978+ if res:
3979+ return (major.value, minor.value)
3980+ return None
3981+
3982+
3983+def has_extension():
3984+ """Returns True if the xrandr extension is available"""
3985+ if XRANDR_VERSION:
3986+ return True
3987+ return False
3988+
3989+
3990+def _to_gamma(gamma):
3991+ g = rr.XRRAllocGamma(len(gamma[0]))
3992+ for i in range(gamma[0]):
3993+ g.red[i] = gamma[0][i]
3994+ g.green[i] = gamma[1][i]
3995+ g.blue[i] = gamma[2][i]
3996+ return g
3997+
3998+
3999+def _from_gamma(g):
4000+ gamma = ([], [], [])
4001+ for i in range(g.size):
4002+ gamma[0].append(g.red[i])
4003+ gamma[1].append(g.green[i])
4004+ gamma[2].append(g.blue[i])
4005+ rr.XRRFreeGamma(g)
4006+
4007+
4008+def _check_required_version(version):
4009+ """Raises an exception if the given or a later version of xrandr is not
4010+ available"""
4011+ if XRANDR_VERSION == None or XRANDR_VERSION < version:
4012+ raise UnsupportedRRError(version, XRANDR_VERSION)
4013+
4014+
4015+def get_mode_height(mode, rotation):
4016+ """Return the height of the given mode taking the rotation into account"""
4017+ if rotation & (RR_ROTATE_0 | RR_ROTATE_180):
4018+ return mode.height
4019+ elif rotation & (RR_ROTATE_90 | RR_ROTATE_270):
4020+ return mode.width
4021+ else:
4022+ return 0
4023+
4024+
4025+def get_mode_width(mode, rotation):
4026+ """Return the width of the given mode taking the rotation into account"""
4027+ if rotation & (RR_ROTATE_0 | RR_ROTATE_180):
4028+ return mode.width
4029+ elif rotation & (RR_ROTATE_90 | RR_ROTATE_270):
4030+ return mode.height
4031+ else:
4032+ return 0
4033+
4034+
4035+XRANDR_VERSION = get_version()
4036+
4037+# vim:ts=4:sw=4:et
4038
4039=== added directory 'checkbox/dbus'
4040=== added file 'checkbox/dbus/__init__.py'
4041--- checkbox/dbus/__init__.py 1970-01-01 00:00:00 +0000
4042+++ checkbox/dbus/__init__.py 2013-10-01 18:38:21 +0000
4043@@ -0,0 +1,89 @@
4044+# This file is part of Checkbox.
4045+#
4046+# Copyright 2012 Canonical Ltd.
4047+# Written by:
4048+# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
4049+#
4050+# Checkbox is free software: you can redistribute it and/or modify
4051+# it under the terms of the GNU General Public License as published by
4052+# the Free Software Foundation, either version 3 of the License, or
4053+# (at your option) any later version.
4054+#
4055+# Checkbox is distributed in the hope that it will be useful,
4056+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4057+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4058+# GNU General Public License for more details.
4059+#
4060+# You should have received a copy of the GNU General Public License
4061+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
4062+#
4063+"""
4064+checkbox.dbus
4065+=============
4066+
4067+Utility modules for working with various things accessible over dbus
4068+"""
4069+
4070+import logging
4071+
4072+from dbus import SystemBus
4073+from dbus.mainloop.glib import DBusGMainLoop
4074+from dbus import (Array, Boolean, Byte, Dictionary, Double, Int16, Int32,
4075+ Int64, ObjectPath, String, Struct, UInt16, UInt32, UInt64)
4076+from gi.repository import GObject
4077+
4078+
4079+def connect_to_system_bus():
4080+ """
4081+ Connect to the system bus properly.
4082+
4083+ Returns a tuple (system_bus, loop) where loop is a GObject.MainLoop
4084+ instance. The loop is there so that you can listen to signals.
4085+ """
4086+ # We'll need an event loop to observe signals. We will need the instance
4087+ # later below so let's keep it. Note that we're not passing it directly
4088+ # below as DBus needs specific API. The DBusGMainLoop class that we
4089+ # instantiate and pass is going to work with this instance transparently.
4090+ #
4091+ # NOTE: DBus tutorial suggests that we should create the loop _before_
4092+ # connecting to the bus.
4093+ logging.debug("Setting up glib-based event loop")
4094+ loop = GObject.MainLoop()
4095+ # Let's get the system bus object. We need that to access UDisks2 object
4096+ logging.debug("Connecting to DBus system bus")
4097+ system_bus = SystemBus(mainloop=DBusGMainLoop())
4098+ return system_bus, loop
4099+
4100+
4101+def drop_dbus_type(value):
4102+ """
4103+ Convert types from the DBus bindings to their python counterparts.
4104+
4105+ This function is mostly lossless, except for arrays of bytes (DBus
4106+ signature "y") that are transparently converted to strings, assuming
4107+ an UTF-8 encoded string.
4108+
4109+ The point of this function is to simplify printing of nested DBus data that
4110+ gets displayed in a rather illegible way.
4111+ """
4112+ if isinstance(value, Array) and value.signature == "y":
4113+ # Some other things are reported as array of bytes that are just
4114+ # strings but due to Unix heritage the encoding is not known.
4115+ # In practice it is better to treat them as UTF-8 strings
4116+ return bytes(value).decode("UTF-8", "replace").strip("\0")
4117+ elif isinstance(value, (Struct, Array)):
4118+ return [drop_dbus_type(item) for item in value]
4119+ elif isinstance(value, (Dictionary)):
4120+ return {drop_dbus_type(dict_key): drop_dbus_type(dict_value)
4121+ for dict_key, dict_value in value.items()}
4122+ elif isinstance(value, (String, ObjectPath)):
4123+ return str(value)
4124+ elif isinstance(value, (Byte, UInt16, UInt32, UInt64,
4125+ Int16, Int32, Int64)):
4126+ return int(value)
4127+ elif isinstance(value, Boolean):
4128+ return bool(value)
4129+ elif isinstance(value, Double):
4130+ return float(value)
4131+ else:
4132+ return value
4133
4134=== added file 'checkbox/dbus/udisks2.py'
4135--- checkbox/dbus/udisks2.py 1970-01-01 00:00:00 +0000
4136+++ checkbox/dbus/udisks2.py 2013-10-01 18:38:21 +0000
4137@@ -0,0 +1,479 @@
4138+# Copyright 2012 Canonical Ltd.
4139+# Written by:
4140+# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
4141+#
4142+# This program is free software: you can redistribute it and/or modify
4143+# it under the terms of the GNU General Public License version 3,
4144+# as published by the Free Software Foundation.
4145+#
4146+# This program is distributed in the hope that it will be useful,
4147+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4148+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4149+# GNU General Public License for more details.
4150+#
4151+# You should have received a copy of the GNU General Public License
4152+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4153+
4154+"""
4155+checkbox.dbus.udisks2
4156+=====================
4157+
4158+Module for working with UDisks2 from python.
4159+
4160+There are two main classes that are interesting here.
4161+
4162+The first class is UDisksObserver, which is easy to setup and has pythonic API
4163+to all of the stuff that happens in UDisks2. It offers simple signal handlers
4164+for any changes that occur in UDisks2 that were advertised by DBus.
4165+
4166+The second class is UDisksModel, that builds on the observer class to offer
4167+persistent collection of objects managed by UDisks2.
4168+
4169+To work with this model you will likely want to look at:
4170+ http://udisks.freedesktop.org/docs/latest/ref-dbus.html
4171+"""
4172+
4173+import logging
4174+
4175+from dbus import Interface, PROPERTIES_IFACE
4176+from dbus.exceptions import DBusException
4177+
4178+from checkbox.dbus import drop_dbus_type
4179+
4180+__all__ = ['UDisks2Observer', 'UDisks2Model', 'Signal', 'is_udisks2_supported',
4181+ 'lookup_udev_device']
4182+
4183+
4184+def is_udisks2_supported(system_bus):
4185+ """
4186+ Check if udisks2 is available on the system bus.
4187+
4188+ ..note::
4189+ Calling this _may_ trigger activation of the UDisks2 daemon but it
4190+ should only happen on systems where it is already expected to run all
4191+ the time.
4192+ """
4193+ observer = UDisks2Observer()
4194+ try:
4195+ logging.debug("Trying to connect to UDisks2...")
4196+ observer.connect_to_bus(system_bus)
4197+ except DBusException as exc:
4198+ if exc.get_dbus_name() == "org.freedesktop.DBus.Error.ServiceUnknown":
4199+ logging.debug("No UDisks2 on the system bus")
4200+ return False
4201+ else:
4202+ raise
4203+ else:
4204+ logging.debug("Got UDisks2 connection")
4205+ return True
4206+
4207+
4208+def map_udisks1_connection_bus(udisks1_connection_bus):
4209+ """
4210+ Map the value of udisks1 ConnectionBus property to the corresponding values
4211+ in udisks2. This a lossy function as some values are no longer supported.
4212+
4213+ Incorrect values raise LookupError
4214+ """
4215+ return {
4216+ 'ata_serial_esata': '', # gone from udisks2
4217+ 'firewire': 'ieee1394', # renamed
4218+ 'scsi': '', # gone from udisks2
4219+ 'sdio': 'sdio', # as-is
4220+ 'usb': 'usb', # as-is
4221+ }[udisks1_connection_bus]
4222+
4223+
4224+def lookup_udev_device(udisks2_object, udev_devices):
4225+ """
4226+ Find the udev_device that corresponds to the udisks2 object
4227+
4228+ Devices are matched by unix filesystem path of the special file (device).
4229+ The udisks2_object must implement the block device interface (so that the
4230+ block device path can be determined) or a ValueError is raised.
4231+
4232+ The udisks2_object must be the dictionary that maps from interface names to
4233+ dictionaries of properties. For compatible data see
4234+ UDisks2Model.managed_objects The udev_devices must be a list of udev
4235+ device, as returned from GUdev.
4236+
4237+ If there is no match, LookupError is raised with the unix block device
4238+ path.
4239+ """
4240+ try:
4241+ block_props = udisks2_object[UDISKS2_BLOCK_INTERFACE]
4242+ except KeyError:
4243+ raise ValueError("udisks2_object must be a block device")
4244+ else:
4245+ block_dev = block_props['Device']
4246+ for udev_device in udev_devices:
4247+ if udev_device.get_device_file() == block_dev:
4248+ return udev_device
4249+ raise LookupError(block_dev)
4250+
4251+
4252+# The well-known name for the ObjectManager interface, sadly it is not a part
4253+# of the python binding along with the rest of well-known names.
4254+OBJECT_MANAGER_INTERFACE = "org.freedesktop.DBus.ObjectManager"
4255+
4256+# The well-known name of the filesystem interface implemented by certain
4257+# objects exposed by UDisks2
4258+UDISKS2_FILESYSTEM_INTERFACE = "org.freedesktop.UDisks2.Filesystem"
4259+
4260+# The well-known name of the block (device) interface implemented by certain
4261+# objects exposed by UDisks2
4262+UDISKS2_BLOCK_INTERFACE = "org.freedesktop.UDisks2.Block"
4263+
4264+# The well-known name of the drive interface implemented by certain objects
4265+# exposed by UDisks2
4266+UDISKS2_DRIVE_INTERFACE = "org.freedesktop.UDisks2.Drive"
4267+
4268+
4269+class Signal:
4270+ """
4271+ Basic signal that supports arbitrary listeners.
4272+
4273+ While this class can be used directly it is best used with the helper
4274+ decorator Signal.define on a member function. The function body is ignored,
4275+ apart from the documentation.
4276+
4277+ The function name then becomes a unique (per encapsulating class instance)
4278+ object (an instance of this Signal class) that is created on demand.
4279+
4280+ In practice you just have a documentation and use
4281+ object.signal_name.connect() and object.signal_name(*args, **kwargs) to
4282+ fire it.
4283+ """
4284+
4285+ def __init__(self, signal_name):
4286+ """
4287+ Construct a signal with the given name
4288+ """
4289+ self._listeners = []
4290+ self._signal_name = signal_name
4291+
4292+ def connect(self, listener):
4293+ """
4294+ Connect a new listener to this signal
4295+
4296+ That listener will be called whenever fire() is invoked on the signal
4297+ """
4298+ self._listeners.append(listener)
4299+
4300+ def disconnect(self, listener):
4301+ """
4302+ Disconnect an existing listener from this signal
4303+ """
4304+ self._listeners.remove(listener)
4305+
4306+ def fire(self, args, kwargs):
4307+ """
4308+ Fire this signal with the specified arguments and keyword arguments.
4309+
4310+ Typically this is used by using __call__() on this object which is more
4311+ natural as it does all the argument packing/unpacking transparently.
4312+ """
4313+ for listener in self._listeners:
4314+ listener(*args, **kwargs)
4315+
4316+ def __call__(self, *args, **kwargs):
4317+ """
4318+ Call fire() with all arguments forwarded transparently
4319+ """
4320+ self.fire(args, kwargs)
4321+
4322+ @classmethod
4323+ def define(cls, dummy_func):
4324+ """
4325+ Helper decorator to define a signal descriptor in a class
4326+
4327+ The decorated function is never called but is used to get
4328+ documentation.
4329+ """
4330+ return _SignalDescriptor(dummy_func)
4331+
4332+
4333+class _SignalDescriptor:
4334+ """
4335+ Descriptor for convenient signal access.
4336+
4337+ Typically this class is used indirectly, when accessed from Signal.define
4338+ method decorator. It is used to do all the magic required when accessing
4339+ signal name on a class or instance.
4340+ """
4341+
4342+ def __init__(self, dummy_func):
4343+ self.signal_name = dummy_func.__name__
4344+ self.__doc__ = dummy_func.__doc__
4345+
4346+ def __repr__(self):
4347+ return "<SignalDecorator for signal: %r>" % self.signal_name
4348+
4349+ def __get__(self, instance, owner):
4350+ if instance is None:
4351+ return self
4352+ # Ensure that the instance has __signals__ property
4353+ if not hasattr(instance, "__signals__"):
4354+ instance.__signals__ = {}
4355+ if self.signal_name not in instance.__signals__:
4356+ instance.__signals__[self.signal_name] = Signal(self.signal_name)
4357+ return instance.__signals__[self.signal_name]
4358+
4359+ def __set__(self, instance, value):
4360+ raise AttributeError("You cannot overwrite signals")
4361+
4362+ def __delete__(self, instance):
4363+ raise AttributeError("You cannot delete signals")
4364+
4365+
4366+class UDisks2Observer:
4367+ """
4368+ Class for observing ongoing changes in UDisks2
4369+ """
4370+
4371+ def __init__(self):
4372+ """
4373+ Create a UDisks2 model.
4374+
4375+ The model must be connected to a bus before it is first used, see
4376+ connect()
4377+ """
4378+ # Proxy to the UDisks2 object
4379+ self._udisks2_obj = None
4380+ # Proxy to the ObjectManager interface exposed by UDisks2 object
4381+ self._udisks2_obj_manager = None
4382+
4383+ @Signal.define
4384+ def on_initial_objects(self, managed_objects):
4385+ """
4386+ Signal fired when the initial list of objects becomes available
4387+ """
4388+
4389+ @Signal.define
4390+ def on_interfaces_added(self, object_path, interfaces_and_properties):
4391+ """
4392+ Signal fired when one or more interfaces gets added to a specific
4393+ object.
4394+ """
4395+
4396+ @Signal.define
4397+ def on_interfaces_removed(self, object_path, interfaces):
4398+ """
4399+ Signal fired when one or more interface gets removed from a specific
4400+ object
4401+ """
4402+
4403+ @Signal.define
4404+ def on_properties_changed(self, interface_name, changed_properties,
4405+ invalidated_properties, sender=None):
4406+ """
4407+ Signal fired when one or more property changes value or becomes
4408+ invalidated.
4409+ """
4410+
4411+ def connect_to_bus(self, bus):
4412+ """
4413+ Establish initial connection to UDisks2 on the specified DBus bus.
4414+
4415+ This will also load the initial set of objects from UDisks2 and thus
4416+ fire the on_initial_objects() signal from the model. Please call this
4417+ method only after connecting that signal if you want to observe that
4418+ event.
4419+ """
4420+ # Once everything is ready connect to udisks2
4421+ self._connect_to_udisks2(bus)
4422+ # And read all the initial objects and setup
4423+ # change event handlers
4424+ self._get_initial_objects()
4425+
4426+ def _connect_to_udisks2(self, bus):
4427+ """
4428+ Setup the initial connection to UDisks2
4429+
4430+ This step can fail if UDisks2 is not available and cannot be
4431+ service-activated.
4432+ """
4433+ # Access the /org/freedesktop/UDisks2 object sitting on the
4434+ # org.freedesktop.UDisks2 bus name. This will trigger the necessary
4435+ # activation if udisksd is not running for any reason
4436+ logging.debug("Accessing main UDisks2 object")
4437+ self._udisks2_obj = bus.get_object(
4438+ "org.freedesktop.UDisks2", "/org/freedesktop/UDisks2")
4439+ # Now extract the standard ObjectManager interface so that we can
4440+ # observe and iterate the collection of objects that UDisks2 provides.
4441+ logging.debug("Accessing ObjectManager interface on UDisks2 object")
4442+ self._udisks2_obj_manager = Interface(
4443+ self._udisks2_obj, OBJECT_MANAGER_INTERFACE)
4444+ # Connect to the PropertiesChanged signal. Here unlike before we want
4445+ # to listen to all signals, regardless of who was sending them in the
4446+ # first place.
4447+ logging.debug("Setting up DBus signal handler for PropertiesChanged")
4448+ bus.add_signal_receiver(
4449+ self._on_properties_changed,
4450+ signal_name="PropertiesChanged",
4451+ dbus_interface=PROPERTIES_IFACE,
4452+ # Use the sender_keyword keyword argument to indicate that we wish
4453+ # to know the sender of each signal. For consistency with other
4454+ # signals we choose to use the 'object_path' keyword argument.
4455+ sender_keyword='sender')
4456+
4457+ def _get_initial_objects(self):
4458+ """
4459+ Get the initial collection of objects.
4460+
4461+ Needs to be called before the first signals from DBus are observed.
4462+ Requires a working connection to UDisks2.
4463+ """
4464+ # Having this interface we can now peek at the existing objects.
4465+ # We can use the standard method GetManagedObjects() to do that
4466+ logging.debug("Accessing GetManagedObjects() on UDisks2 object")
4467+ managed_objects = self._udisks2_obj_manager.GetManagedObjects()
4468+ managed_objects = drop_dbus_type(managed_objects)
4469+ # Fire the public signal for getting initial objects
4470+ self.on_initial_objects(managed_objects)
4471+ # Connect our internal handles to the DBus signal handlers
4472+ logging.debug("Setting up DBus signal handler for InterfacesAdded")
4473+ self._udisks2_obj_manager.connect_to_signal(
4474+ "InterfacesAdded", self._on_interfaces_added)
4475+ logging.debug("Setting up DBus signal handler for InterfacesRemoved")
4476+ self._udisks2_obj_manager.connect_to_signal(
4477+ "InterfacesRemoved", self._on_interfaces_removed)
4478+
4479+ def _on_interfaces_added(self, object_path, interfaces_and_properties):
4480+ """
4481+ Internal callback that is called by DBus
4482+
4483+ This function is responsible for firing the public signal
4484+ """
4485+ # Convert from dbus types
4486+ object_path = drop_dbus_type(object_path)
4487+ interfaces_and_properties = drop_dbus_type(interfaces_and_properties)
4488+ # Log what's going on
4489+ logging.debug("The object %r has gained the following interfaces and "
4490+ "properties: %r", object_path, interfaces_and_properties)
4491+ # Call the signal handler
4492+ self.on_interfaces_added(object_path, interfaces_and_properties)
4493+
4494+ def _on_interfaces_removed(self, object_path, interfaces):
4495+ """
4496+ Internal callback that is called by DBus
4497+
4498+ This function is responsible for firing the public signal
4499+ """
4500+ # Convert from dbus types
4501+ object_path = drop_dbus_type(object_path)
4502+ interfaces = drop_dbus_type(interfaces)
4503+ # Log what's going on
4504+ logging.debug("The object %r has lost the following interfaces: %r",
4505+ object_path, interfaces)
4506+ # Call the signal handler
4507+ self.on_interfaces_removed(object_path, interfaces)
4508+
4509+ def _on_properties_changed(self, interface_name, changed_properties,
4510+ invalidated_properties, sender=None):
4511+ """
4512+ Internal callback that is called by DBus
4513+
4514+ This function is responsible for firing the public signal
4515+ """
4516+ # Convert from dbus types
4517+ interface_name = drop_dbus_type(interface_name)
4518+ changed_properties = drop_dbus_type(changed_properties)
4519+ invalidated_properties = drop_dbus_type(invalidated_properties)
4520+ sender = drop_dbus_type(sender)
4521+ # Log what's going on
4522+ logging.debug("Some object with the interface %r has changed "
4523+ "properties: %r and invalidated properties %r "
4524+ "(sender: %s)",
4525+ interface_name, changed_properties,
4526+ invalidated_properties, sender)
4527+ # Call the signal handler
4528+ self.on_properties_changed(interface_name, changed_properties,
4529+ invalidated_properties, sender)
4530+
4531+
4532+class UDisks2Model:
4533+ """
4534+ Model for working with UDisks2
4535+
4536+ This class maintains a persistent model of what UDisks2 knows about, based
4537+ on the UDisks2Observer class and the signals it offers.
4538+ """
4539+
4540+ def __init__(self, observer):
4541+ """
4542+ Create a UDisks2 model.
4543+
4544+ The model will track changes using the specified observer (which is
4545+ expected to be a UDisks2Observer instance)
4546+
4547+ You should only connect the observer to the bus after creating the
4548+ model otherwise the initial objects will not be detected.
4549+ """
4550+ # Local state, everything that UDisks2 tells us
4551+ self._managed_objects = {}
4552+ self._observer = observer
4553+ # Connect all the signals to the observer
4554+ self._observer.on_initial_objects.connect(self._on_initial_objects)
4555+ self._observer.on_interfaces_added.connect(self._on_interfaces_added)
4556+ self._observer.on_interfaces_removed.connect(
4557+ self._on_interfaces_removed)
4558+ self._observer.on_properties_changed.connect(
4559+ self._on_properties_changed)
4560+
4561+ @Signal.define
4562+ def on_change(self):
4563+ """
4564+ Signal sent whenever the collection of managed object changes
4565+
4566+ Note that this signal is fired _after_ the change has occurred
4567+ """
4568+
4569+ @property
4570+ def managed_objects(self):
4571+ """
4572+ A collection of objects that is managed by this model. All changes as
4573+ well as the initial state, are reflected here.
4574+ """
4575+ return self._managed_objects
4576+
4577+ def _on_initial_objects(self, managed_objects):
4578+ """
4579+ Internal callback called when we get the initial collection of objects
4580+ """
4581+ self._managed_objects = drop_dbus_type(managed_objects)
4582+
4583+ def _on_interfaces_added(self, object_path, interfaces_and_properties):
4584+ """
4585+ Internal callback called when an interface is added to certain object
4586+ """
4587+ # Update internal state
4588+ if object_path not in self._managed_objects:
4589+ self._managed_objects[object_path] = {}
4590+ obj = self._managed_objects[object_path]
4591+ obj.update(interfaces_and_properties)
4592+ # Fire the change signal
4593+ self.on_change()
4594+
4595+ def _on_interfaces_removed(self, object_path, interfaces):
4596+ """
4597+ Internal callback called when an interface is removed from a certain
4598+ object
4599+ """
4600+ # Update internal state
4601+ if object_path in self._managed_objects:
4602+ obj = self._managed_objects[object_path]
4603+ for interface in interfaces:
4604+ if interface in obj:
4605+ del obj[interface]
4606+ # Fire the change signal
4607+ self.on_change()
4608+
4609+ def _on_properties_changed(self, interface_name, changed_properties,
4610+ invalidated_properties, sender=None):
4611+ # XXX: This is a workaround the fact that we cannot
4612+ # properly track changes to all properties :-(
4613+ self._managed_objects = drop_dbus_type(
4614+ self._observer._udisks2_obj_manager.GetManagedObjects())
4615+ # Fire the change signal()
4616+ self.on_change()
4617
4618=== added file 'checkbox/dispatcher.py'
4619--- checkbox/dispatcher.py 1970-01-01 00:00:00 +0000
4620+++ checkbox/dispatcher.py 2013-10-01 18:38:21 +0000
4621@@ -0,0 +1,216 @@
4622+#
4623+# This file is part of Checkbox.
4624+#
4625+# Copyright 2010-12 Canonical Ltd.
4626+#
4627+# Checkbox is free software: you can redistribute it and/or modify
4628+# it under the terms of the GNU General Public License as published by
4629+# the Free Software Foundation, either version 3 of the License, or
4630+# (at your option) any later version.
4631+#
4632+# Checkbox is distributed in the hope that it will be useful,
4633+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4634+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4635+# GNU General Public License for more details.
4636+#
4637+# You should have received a copy of the GNU General Public License
4638+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
4639+#
4640+__metaclass__ = type
4641+
4642+__all__ = [
4643+ "Dispatcher",
4644+ "DispatcherList",
4645+ "DispatcherQueue",
4646+ ]
4647+
4648+import logging
4649+
4650+from itertools import product
4651+
4652+
4653+class Event:
4654+ """Event payload containing the positional and keywoard arguments
4655+ passed to the handler in the event listener."""
4656+
4657+ def __init__(self, type, *args, **kwargs):
4658+ self.type = type
4659+ self.args = args
4660+ self.kwargs = kwargs
4661+
4662+
4663+class Listener:
4664+ """Event listener notified when events are published by the dispatcher."""
4665+
4666+ def __init__(self, event_type, handler, count):
4667+ self.event_type = event_type
4668+ self.handler = handler
4669+ self.count = count
4670+
4671+ def notify(self, event):
4672+ """Notify the handler with the payload of the event.
4673+
4674+ :param event: The event containint the payload for the handler.
4675+ """
4676+ if self.count is None or self.count:
4677+ self.handler(*event.args, **event.kwargs)
4678+ if self.count:
4679+ self.count -= 1
4680+
4681+
4682+class ListenerList(Listener):
4683+ """Event listener notified for lists of events."""
4684+
4685+ def __init__(self, *args, **kwargs):
4686+ super(ListenerList, self).__init__(*args, **kwargs)
4687+ self.event_types = set(self.event_type)
4688+ self.kwargs = {}
4689+
4690+ def notify(self, event):
4691+ """Only notify the handler when all the events for this listener
4692+ have been published by the dispatcher. When duplicate events
4693+ occur, the latest event is preserved and the previous one are
4694+ overwritten until all events have been published.
4695+ """
4696+ if self.count is None or self.count:
4697+ self.kwargs[event.type] = event.args[0]
4698+ if self.event_types.issubset(self.kwargs):
4699+ self.handler(**self.kwargs)
4700+ if self.count:
4701+ self.count -= 1
4702+
4703+
4704+class ListenerQueue(ListenerList):
4705+
4706+ def notify(self, event):
4707+ """Only notify the handler when all the events for this listener
4708+ have been published by the dispatcher. Duplicate events are enqueued
4709+ and dequeued only when all events have been published.
4710+ """
4711+ arg = event.args[0]
4712+ queue = self.kwargs.setdefault(event.type, [])
4713+
4714+ # Strip duplicates from the queue.
4715+ if arg not in queue:
4716+ queue.append(arg)
4717+
4718+ # Once the queue has handler has been called, the queue
4719+ # then behaves like a list using the latest events.
4720+ if self.event_types.issubset(self.kwargs):
4721+ self.notify = notify = super(ListenerQueue, self).notify
4722+ keys = list(self.kwargs.keys())
4723+ for values in product(*list(self.kwargs.values())):
4724+ self.kwargs = dict(list(zip(keys, values)))
4725+ notify(event)
4726+
4727+
4728+class Dispatcher:
4729+ """Register handlers and publish events for them identified by strings."""
4730+
4731+ listener_factory = Listener
4732+
4733+ def __init__(self, listener_factory=None):
4734+ self._event_listeners = {}
4735+
4736+ if listener_factory is not None:
4737+ self.listener_factory = listener_factory
4738+
4739+ def registerHandler(self, event_type, handler, count=None):
4740+ """Register an event handler and return its listener.
4741+
4742+ :param event_type: The name of the event type to handle.
4743+ :param handler: The function handling the given event type.
4744+ :param count: Optionally, the number times to call the handler.
4745+ """
4746+ listener = self.listener_factory(event_type, handler, count)
4747+
4748+ listeners = self._event_listeners.setdefault(event_type, [])
4749+ listeners.append(listener)
4750+
4751+ return listener
4752+
4753+ def unregisterHandler(self, handler):
4754+ """Unregister a handler.
4755+
4756+ :param handler: The handler to unregister.
4757+ """
4758+ for event_type, listeners in self._event_listeners.items():
4759+ listeners = [
4760+ listener for listener in listeners
4761+ if listener.handler == handler]
4762+ if listeners:
4763+ self._event_listeners[event_type] = listeners
4764+ else:
4765+ del self._event_listeners[event_type]
4766+
4767+ def unregisterListener(self, listener, event_type=None):
4768+ """Unregister a listener.
4769+
4770+ :param listener: The listener of the handler to unregister.
4771+ :param event_type: Optionally, the event_type to unregister.
4772+ """
4773+ if event_type is None:
4774+ event_type = listener.event_type
4775+
4776+ self._event_listeners[event_type].remove(listener)
4777+ if not self._event_listeners[event_type]:
4778+ del self._event_listeners[event_type]
4779+
4780+ def publishEvent(self, event_type, *args, **kwargs):
4781+ """Publish an event of a given type and notify all listeners.
4782+
4783+ :param event_type: The name of the event type to publish.
4784+ :param args: Positional arguments to pass to the registered handlers.
4785+ :param kwargs: Keyword arguments to pass to the registered handlers.
4786+ """
4787+ if event_type in self._event_listeners:
4788+ event = Event(event_type, *args, **kwargs)
4789+ for listener in list(self._event_listeners[event_type]):
4790+ try:
4791+ listener.notify(event)
4792+ if listener.count is not None and not listener.count:
4793+ self.unregisterListener(listener)
4794+ except:
4795+ logging.exception(
4796+ "Error running event handler for %r with args %r %r",
4797+ event_type, args, kwargs)
4798+
4799+
4800+class DispatcherList(Dispatcher):
4801+ """
4802+ Register handlers and publish events for them identified by lists
4803+ of strings.
4804+ """
4805+
4806+ listener_factory = ListenerList
4807+
4808+ def registerHandler(self, event_types, handler, count=None):
4809+ """See Dispatcher."""
4810+ if not isinstance(event_types, (list, tuple)):
4811+ event_types = (event_types,)
4812+
4813+ listener = self.listener_factory(event_types, handler, count)
4814+ for event_type in event_types:
4815+ listeners = self._event_listeners.setdefault(event_type, [])
4816+ listeners.append(listener)
4817+
4818+ return listener
4819+
4820+ def unregisterListener(self, listener):
4821+ """See Dispatcher."""
4822+ for event_type in listener.event_types:
4823+ super(DispatcherList, self).unregisterListener(
4824+ listener, event_type)
4825+
4826+ def publishEvent(self, event_type, arg):
4827+ """See Dispatcher."""
4828+ super(DispatcherList, self).publishEvent(event_type, arg)
4829+
4830+
4831+class DispatcherQueue(DispatcherList):
4832+ """
4833+ Register handlers and publish events for them identified by lists
4834+ of strings in queue order.
4835+ """
4836+
4837+ listener_factory = ListenerQueue
4838
4839=== added directory 'checkbox/heuristics'
4840=== added file 'checkbox/heuristics/__init__.py'
4841--- checkbox/heuristics/__init__.py 1970-01-01 00:00:00 +0000
4842+++ checkbox/heuristics/__init__.py 2013-10-01 18:38:21 +0000
4843@@ -0,0 +1,56 @@
4844+# This file is part of Checkbox.
4845+#
4846+# Copyright 2012 Canonical Ltd.
4847+# Written by:
4848+# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
4849+#
4850+# Checkbox is free software: you can redistribute it and/or modify
4851+# it under the terms of the GNU General Public License as published by
4852+# the Free Software Foundation, either version 3 of the License, or
4853+# (at your option) any later version.
4854+#
4855+# Checkbox is distributed in the hope that it will be useful,
4856+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4857+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4858+# GNU General Public License for more details.
4859+#
4860+# You should have received a copy of the GNU General Public License
4861+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
4862+
4863+"""
4864+checkbox.heuristics
4865+===================
4866+
4867+This module contains implementations behind various heuristics used throughout
4868+the code. The intent of this module is twofold:
4869+
4870+ 1) To encourage code reuse so that developers can use one implementation of
4871+ "guesswork" that is sometimes needed in our test. This reduces duplicate
4872+ bugs where many scripts do similar things in a different way.
4873+
4874+ 2) To identify missing features in plumbing layer APIs such as
4875+ udev/udisks/dbus etc. Ideally no program should have to guess this, the
4876+ plumbing layer should be able to provide this meta data to allow
4877+ application developers deliver consistent behavior across userspace.
4878+
4879+Heuristics should be reusable from both python and shell. To make that possible
4880+each heuristics needs to be constrained to serializable input and output. This
4881+levels the playing field and allows both shell developers and python developers
4882+to reuse the same function.
4883+
4884+Additionally heuristics should try to avoid accessing thick APIs (such as
4885+objects returned by various libraries. This is meant to decrease the likelihood
4886+that updates to those libraries break this code. As an added side effect this
4887+also should make the implementation more explicit and easier to understand.
4888+
4889+In the long term each heuristic should be discussed with upstream developers of
4890+the particular problem area (udev, udisks, etc) to see if that subsystem can
4891+provide the required information directly, without us having to guess and fill
4892+the gaps.
4893+
4894+Things to consider when adding entries to this package:
4895+
4896+ 1) File a bug on the upstream package about missing feature.
4897+
4898+ 2) File a bug on checkbox to de-duplicate similar heuristics
4899+"""
4900
4901=== added directory 'checkbox/heuristics/tests'
4902=== added file 'checkbox/heuristics/tests/__init__.py'
4903=== added file 'checkbox/heuristics/tests/test_udisks2.py'
4904--- checkbox/heuristics/tests/test_udisks2.py 1970-01-01 00:00:00 +0000
4905+++ checkbox/heuristics/tests/test_udisks2.py 2013-10-01 18:38:21 +0000
4906@@ -0,0 +1,40 @@
4907+# This file is part of Checkbox.
4908+#
4909+# Copyright 2012 Canonical Ltd.
4910+# Written by:
4911+# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
4912+#
4913+# Checkbox is free software: you can redistribute it and/or modify
4914+# it under the terms of the GNU General Public License as published by
4915+# the Free Software Foundation, either version 3 of the License, or
4916+# (at your option) any later version.
4917+#
4918+# Checkbox is distributed in the hope that it will be useful,
4919+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4920+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4921+# GNU General Public License for more details.
4922+#
4923+# You should have received a copy of the GNU General Public License
4924+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
4925+
4926+"""
4927+
4928+checkbox.heuristics.tests.test_udisks2
4929+======================================
4930+
4931+Unit tests for checkbox.heuristics.udisks2 module
4932+"""
4933+
4934+from unittest import TestCase
4935+
4936+from checkbox.heuristics.udisks2 import is_memory_card
4937+
4938+
4939+class TestIsMemoryCard(TestCase):
4940+
4941+ def test_generic(self):
4942+ """
4943+ Device with vendor string "GENERIC" is a memory card
4944+ """
4945+ self.assertTrue(
4946+ is_memory_card(vendor="Generic", model="", udisks2_media=None))
4947
4948=== added file 'checkbox/heuristics/udev.py'
4949--- checkbox/heuristics/udev.py 1970-01-01 00:00:00 +0000
4950+++ checkbox/heuristics/udev.py 2013-10-01 18:38:21 +0000
4951@@ -0,0 +1,44 @@
4952+# This file is part of Checkbox.
4953+#
4954+# Copyright 2012 Canonical Ltd.
4955+# Written by:
4956+# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
4957+#
4958+# Checkbox is free software: you can redistribute it and/or modify
4959+# it under the terms of the GNU General Public License as published by
4960+# the Free Software Foundation, either version 3 of the License, or
4961+# (at your option) any later version.
4962+#
4963+# Checkbox is distributed in the hope that it will be useful,
4964+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4965+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4966+# GNU General Public License for more details.
4967+#
4968+# You should have received a copy of the GNU General Public License
4969+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
4970+
4971+"""
4972+checkbox.heuristics.dev
4973+=======================
4974+
4975+Heuristics for udev.
4976+
4977+ Documentation: http://udisks.freedesktop.org/docs/latest/
4978+ Source code: http://cgit.freedesktop.org/systemd/systemd/ (src/udev)
4979+ Bug tracker: http://bugs.freedesktop.org/ (using systemd product)
4980+"""
4981+
4982+
4983+def is_virtual_device(device_file):
4984+ """
4985+ Given a device name like /dev/ramX, /dev/sdX or /dev/loopX determine if
4986+ this is a virtual device. Virtual devices are typically uninteresting to
4987+ users. The only exception may be nonempty loopback device.
4988+
4989+ Possible prior art: gnome-disks, palimpset (precursor, suffering from this
4990+ flaw and showing all the /dev/ram devices by default)
4991+ """
4992+ for part in device_file.split("/"):
4993+ if part.startswith("ram") or part.startswith("loop"):
4994+ return True
4995+ return False
4996
4997=== added file 'checkbox/heuristics/udisks2.py'
4998--- checkbox/heuristics/udisks2.py 1970-01-01 00:00:00 +0000
4999+++ checkbox/heuristics/udisks2.py 2013-10-01 18:38:21 +0000
5000@@ -0,0 +1,62 @@
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches