diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..30e9fb87e --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,3 @@ + +**Checklist** +- [ ] Add a news fragment if this PR is news-worthy for end users. (see docs/releases/README.rst) \ No newline at end of file diff --git a/CHANGES.txt b/CHANGES.txt index 6cd10f276..bf5d50338 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,153 @@ Traits UI Changelog =================== +Release 7.2.0 +------------- +TraitsUI 7.2.0 is a minor release which includes numerous bug fixes, +documentation improvements, code maintenance changes, and enhancements. + +Highlights of this release +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* The migration from ``on_trait_change`` to ``observe`` is underway. As a + result, TraitsUI now requires Traits >= 6.1. +* New display-only VideoEditor (currently only on Qt backend). +* Exapnsion of features for the new ``UITester`` including the ability to + inspect UI object visibility / enabledness. Also documentation for testing + has been updated. + +Notes on upgrading +~~~~~~~~~~~~~~~~~~ + +* This release of TraitsUI now depends on Traits 6.1+. Also, deprecated code / + modules have been removed. Namely, the :mod:`traitsui.image ` + module which was moved to ``pyface.image``, ``editors_gen`` modules, Editor + and EditorFactory factory methods on Toolkit objects, and more. For a + complete list, see PRs in the "Removals" section below. These were all + generally unused / deprecated for sometime. Also, importing directly from + ``traitsui.editors`` has been deprecated. Please update imports to import + directly from :mod:`traitsui.api` or :mod:`traitsui.editors.api`. + +Detailed changes +~~~~~~~~~~~~~~~~ + +Thanks to: + +* Aaron Ayres +* Kit Yan Choi +* Mark Dickinson +* Rahul Poruri +* Corran Webster + +Enhancements +~~~~~~~~~~~~ + +* Open links externally instead of in the html editor in etsdemo application (#1446) +* Hide demo tab when the demo is None (#1456) +* Extract an interface from TargetRegistry to allow wider testing support for IsEnabled (#1490) +* Expose clearButtonEnabled for Qt TextEditor (#1516) +* Extend / document secret TreeNode api for passing tuples into add list trait (#1527) +* Add 'IsVisible` query class for UI Tester (#1552) +* Extend button editor to allow dynamically changing button image (#1566) +* Support testing with simple FileEditor (without dialog) (#1571) +* Qt VideoEditor (#745, #1609, #1621) +* Add item_factory callable for specifying creation of new list items (#1634) + +Fixes +~~~~~ + +* Support HTMLEditor.open_externally on QtWebEngine (#1451) +* Qt InstanceEditor button should never be the default (#1498) +* Propagate UI errors to the UI's parent (#1503) +* Fix resizable readonly enum editor (#1532) +* StartStyling API changed for new Wx version (#1536) +* Don't use html for a variable name when using html module from standard library (#1540) +* Label {visible/enabled}_when (#1544) +* manually set the text format to plain text in error (#1546) +* Fix RGBColor hex int to tuple color conversion (#1554) +* Make Scrollable group respect visible_when (#1555) +* Use correct source_path in example (#1433) +* Move EnumEditor import in table_filter into the methods that need it (#1616) + +Documentation +~~~~~~~~~~~~~ + +* Add a module docstring to the api modules (#1441) +* Use viewcode sphinx extension (#1443) +* Add links to API docs in a etsdemo examples for traitsui standard editors (#1445) +* Clarify which of traitsui.api and traitsui.editors.api is recommended (#1471) +* traitsui.testing documentation updates (#1482, #1483, #1485, #1486, #1487, #1488) +* Fix link label to the documentation home page (#1489) +* Document enabled_when / visible_when better (#1537) +* Document entries parameter not supported qt for FileEditor/DirectoryEditor (#1557 +* Add script to regenerate screenshots of editors for documentation (#1574) +* Remove unused images in docs (#1584) +* replace # with #: to document traits in traitsui.editors.* (#1596) +* User facing docs for VideoEditor (#1630) +* Format code examples in the user documentation (#1640) + +Build and continuous integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Fix cron job not installing additional dependencies (#1427) +* Remove job on Travis for testing against Traits 6.0 (#1430) +* Drop Python 3.5 support in TraitsUI (#1436) +* Declare Python 3.5 not supported for ets-demo (#1437) +* Fix link for installing wxPython on CI (for etsdemo) (#1491) +* Add GitHub Actions to test against EDM (#1492) +* Add GitHub Actions workflow for testing ets-demo (#1496) +* Use EDM 3.2.3 instead of EDM 3.2.1 (#1548) +* Drop support for PyQt < 4.3.2 (#1607) +* Remove CI test against Traits 6.0 (#1637) + +Test suite +~~~~~~~~~~ + +* Refactor and extend tests for Qt HTMLEditor handling of opening links (#1465) +* Update new test_editor_error_msg (#1553) +* Unskip "EnumEditor" tests that were failing earlier on windows (#1615) +* skip video editor test without numpy / move numpy import to when it is needed + / list numpy as test dependency (#1639) +* skip test if no QtWebkit or QtWebEngine (#1649) + +Maintenance and code organization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Add pygments to etsdemo's etstool.py (#1453) +* Add help aliases to etsdemo etstool module (#1457) +* Fix/Update copyright headers (#1467, #1486) +* work towards flake8 clean codebase (#1469, #1562, #1563) +* Remove meaningless comments from an outdated coding style. (#1472) +* Remove clause that deviates from PEP8 backward compatibility convention in the testing package (#1481) +* Formal editor interface for tooltips (#1493 +* Add instance choice classes to traitsui.api (#1495) +* Undo/Redo cleanup (#1510 +* start on_trait_change to observe migration (#1519, #1520, #1523, #1525, #1545, #1622, #1644) +* Refactor TreeEditor _new_actions and _menu_new_node to avoid hacky eval (#1524) +* Refactor _add_items method of _GroupPanel object (#1549) +* Add new trait to eventually replace scroll_to_row_hint (#1560) +* Add 'default': True to the etsdemo eam metadata (#1568) +* update "super" usage (#1583, #1587, #1588, #1589, #1604) +* Use "str.capitalize" directly instead of an alias (#1598) +* Use "PrefixList" from traits >= 6.1 (#1599) +* Update use of deprecated "Thread.setDaemon" (#1601) +* Replace deprecated "wx.ListItemAttr" with "wx.ItemAttr" (#1602) +* Adds "traitsui.toolkit.toolkit_object" to "traitsui.api" (#1603) +* Import "TraitFactory" from "traits.api" (#1606) +* Rename editor factory classes (#1610) +* Cleanup imports in "traitsui.editors.*" (#1619) + +Removals +~~~~~~~~ + +* Remove deprecated classes/modules (#1594) +* remove traitsui.image submodule (#1595) +* Remove Editor and EditorFactory factory methods on Toolkit objects (#1600) +* Remove backwards compatibility toolkit factory imports (#1608) +* Remove unused "Item.full_size" trait (#1613) +* Remove code handling old/outdated versions of wx (#1614) +* Remove unused editors_gen modules (#1626) + Release 7.1.1 ------------- @@ -18,7 +165,7 @@ Fixes Release 7.1.0 ------------- -TraitsUI is a minor feature release which introduces a new testing library +TraitsUI 7.1.0 is a minor feature release which introduces a new testing library and a number of significant fixes. Demo examples are also distributed as package data such that they are visible via the "etsdemo" GUI application (to be installed separately). diff --git a/docs/releases/README.rst b/docs/releases/README.rst new file mode 100644 index 000000000..bf835c671 --- /dev/null +++ b/docs/releases/README.rst @@ -0,0 +1,29 @@ +The `upcoming` directory contains news fragments that will be added to the +changelog for the NEXT release. + +Changes that are not of interest to the end-user can skip adding news fragment. + +Add a news fragment +------------------- +Create a new file with a name like ``..rst``, where +```` is a pull request number, and ```` is one of: + +- ``feature``: New feature +- ``bugfix``: Bug fixes +- ``deprecation``: Deprecations of public API +- ``removal``: Removal of public API +- ``doc``: Documentation changes +- ``test``: Changes to test suite ('end users' are distribution packagers) +- ``build``: Build system changes that affect how the distribution is installed + +Then write a short sentence in the file that describes the changes for the +end users, e.g. in ``123.removal.rst``:: + + Remove package xyz. + +Alternatively, use the following command, run from the project root directory +and answer the questions:: + + python etstool.py changelog create + +(This command requires ``click`` in the environment.) diff --git a/etstool.py b/etstool.py index b4c079642..e3ec02b2d 100644 --- a/etstool.py +++ b/etstool.py @@ -82,6 +82,13 @@ commands for each task. See the EDM documentation for more information about how to run commands within an EDM enviornment. +Build changelog +--------------- +To create a first-cut changelog from the news fragments, use this command:: + + python etstool.py changelog build + +This will update the changelog file. You should review and edit it. """ import glob @@ -186,6 +193,15 @@ 'null': {'ETS_TOOLKIT': 'null'}, } +# Location of documentation files +HERE = os.path.dirname(__file__) +DOCS_DIR = os.path.join(HERE, "docs") + +# Location of news fragment for creating changelog. +NEWS_FRAGMENT_DIR = os.path.join(DOCS_DIR, "releases", "upcoming") + +# Location of the Changelog file. +CHANGELOG_PATH = os.path.join(HERE, "CHANGES.txt") def normalize(name): return name.replace("_", "-") @@ -477,6 +493,112 @@ def flake8(runtime, toolkit, environment): execute(commands, parameters) +@cli.group("changelog") +@click.pass_context +def changelog(ctx): + """ Group of commands related to creating changelog.""" + + ctx.obj = { + # Mapping from news fragment type to their description in + # the changelog. + "type_to_description": { + "feature": "Features", + "bugfix": "Fixes", + "deprecation": "Deprecations", + "removal": "Removals", + "doc": "Documentation changes", + "test": "Test suite", + "build": "Build System", + } + } + + +@changelog.command("create") +@click.pass_context +def create_news_fragment(ctx): + """ Create a news fragment for your PR.""" + + pr_number = click.prompt('Please enter the PR number', type=int) + type_ = click.prompt( + "Choose a fragment type:", + type=click.Choice(ctx.obj["type_to_description"]) + ) + + filepath = os.path.join( + NEWS_FRAGMENT_DIR, f"{pr_number}.{type_}.rst" + ) + + if os.path.exists(filepath): + click.echo("FAILED: File {} already exists.".format(filepath)) + ctx.exit(1) + + content = click.prompt( + "Describe the changes to the END USERS.\n" + "Example: 'Remove subpackage xyz.'\n", + type=str, + ) + if not os.path.exists(NEWS_FRAGMENT_DIR): + os.makedirs(NEWS_FRAGMENT_DIR) + with open(filepath, "w", encoding="utf-8") as fp: + fp.write(content + f" (#{pr_number})") + + click.echo("Please commit the file created at: {}".format(filepath)) + + +@changelog.command("build") +@click.pass_context +def build_changelog(ctx): + """ Build Changelog created from all the news fragments.""" + # This is a rather simple first-cut generation of the changelog. + # It removes the laborious concatenation, but the end results might + # still require some tweaking. + contents = [] + + # Collect news fragment files as we go, and then optionally remove them. + handled_file_paths = [] + + for type_, description in ctx.obj["type_to_description"].items(): + pattern = os.path.join(NEWS_FRAGMENT_DIR, f"*.{type_}.rst") + file_paths = sorted(glob.glob(pattern)) + + if file_paths: + contents.append("") + contents.append(description) + contents.append("-" * len(description)) + + for filename in file_paths: + with open(filename, "r", encoding="utf-8") as fp: + contents.append("* " + fp.read()) + handled_file_paths.append(filename) + + # Prepend content to the changelog file. + + with open(CHANGELOG_PATH, "r", encoding="utf-8") as fp: + original_changelog = fp.read() + + with open(CHANGELOG_PATH, "w", encoding="utf-8") as fp: + if contents: + print(*contents, sep="\n", file=fp) + fp.write(original_changelog) + + click.echo(f"Changelog is updated. Please review it at {CHANGELOG_PATH}") + + # Optionally clean up collected news fragments. + should_clean = click.confirm( + "Do you want to remove the news fragments?" + ) + if should_clean: + for file_path in handled_file_paths: + os.remove(file_path) + + # Report any leftover for developers to inspect. + leftovers = sorted(glob.glob(os.path.join(NEWS_FRAGMENT_DIR, "*"))) + if leftovers: + click.echo("These files are not collected:") + click.echo("\n ".join([""] + leftovers)) + + click.echo("Done") + # ---------------------------------------------------------------------------- # Utility routines # ----------------------------------------------------------------------------